2018年12月9日 星期日

[Linux] dev_appserver.py ERROR module.py:1652] The PHP runtime cannot be run with the "Memcache" PECL extension installed @ Ubuntu 16.04

在 Ubuntu server 上,首推 https://cloud.google.com/sdk/downloads#linux 安裝法,不要靠 apt 套件管理,不然會踩到很多麻煩:

$ curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-183.0.0-linux-x86_64.tar.gz | tar -xvf -
$ cd google-cloud-sdk
$ ./install.sh


裝完後,若是要運行 php-sdk 時,會踩到 php 環境問題,通常就是要指定 --php_executable_path 位置:

$ dev_appserver.py --enable_host_checking false --host 0.0.0.0 --port 8080  --php_executable_path /usr/bin/php-cgi5.6 app.yaml

只是執行時又會踩到缺套件等等的問題,裝到最後產生了 Memcache 套件問題,就要再把 Memcache 套件關閉即可:

$ php5.6 --ini
Configuration File (php.ini) Path: /etc/php/5.6/cli
Loaded Configuration File:         /etc/php/5.6/cli/php.ini
Scan for additional .ini files in: /etc/php/5.6/cli/conf.d
Additional .ini files parsed:      /etc/php/5.6/cli/conf.d/10-opcache.ini,
/etc/php/5.6/cli/conf.d/10-pdo.ini,
/etc/php/5.6/cli/conf.d/20-apcu.ini,
/etc/php/5.6/cli/conf.d/20-bcmath.ini,
/etc/php/5.6/cli/conf.d/20-calendar.ini,
/etc/php/5.6/cli/conf.d/20-ctype.ini,
/etc/php/5.6/cli/conf.d/20-exif.ini,
/etc/php/5.6/cli/conf.d/20-fileinfo.ini,
/etc/php/5.6/cli/conf.d/20-ftp.ini,
/etc/php/5.6/cli/conf.d/20-gettext.ini,
/etc/php/5.6/cli/conf.d/20-iconv.ini,
/etc/php/5.6/cli/conf.d/20-igbinary.ini,
/etc/php/5.6/cli/conf.d/20-json.ini,
/etc/php/5.6/cli/conf.d/20-memcache.ini,
/etc/php/5.6/cli/conf.d/20-msgpack.ini,
/etc/php/5.6/cli/conf.d/20-phar.ini,
/etc/php/5.6/cli/conf.d/20-posix.ini,
/etc/php/5.6/cli/conf.d/20-readline.ini,
/etc/php/5.6/cli/conf.d/20-shmop.ini,
/etc/php/5.6/cli/conf.d/20-sockets.ini,
/etc/php/5.6/cli/conf.d/20-sysvmsg.ini,
/etc/php/5.6/cli/conf.d/20-sysvsem.ini,
/etc/php/5.6/cli/conf.d/20-sysvshm.ini,
/etc/php/5.6/cli/conf.d/20-tokenizer.ini,
/etc/php/5.6/cli/conf.d/25-memcached.ini

$ sudo vim /etc/php/5.6/cli/conf.d/20-memcache.ini
; uncomment the next line to enable the module
;extension=memcache.so

$ sudo vim /etc/php/5.6/cli/conf.d/25-memcached.ini
; priority=25
;extension=memcached.so

2018年12月3日 星期一

Android 開發筆記 - Image Picker 與權限管理

在 Android 6.0 (API:23) 後,就算 AndroidManifest.xml 寫好要權限的部分,但實務上在進行時,依舊要主動要一次,讓用戶感受到真的要權限了:https://developer.android.com/training/permissions/requesting

做 Image Picker 時,若最終要讀取資料,那需要 android.permission.READ_EXTERNAL_STORAGE 權限,以下則是在 MainActivity 運行的範例:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>


public class MainActivity extends AppCompatActivity {
private static final int REQUEST_SELECT_VIDEO = 1;
private static final int REQUEST_EXTERNAL_STORAGE = 2;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
} else {
request_pick();
}
}

void request_pick() {
final Uri mUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
final Intent mIntent = new Intent(Intent.ACTION_PICK, mUri);
final PackageManager mPackageManager = getPackageManager();
List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 1) {
startActivityForResult(
Intent.createChooser(
new Intent(Intent.ACTION_PICK, mUri), "選取圖片"
),
REQUEST_SELECT_VIDEO
);
} else {
startActivityForResult(mIntent, REQUEST_SELECT_VIDEO);
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_SELECT_VIDEO) {
// ...
// 在處理檔案讀取時,缺少 android.permission.READ_EXTERNAL_STORAGE 會造成 IOException:
// open failed: EACCES (Permission denied)
// ...
}
}
}
}