読者です 読者をやめる 読者になる 読者になる

最近ハマった事象の備忘録(SupporLibrary編)

android support-library

はじめに

マテリアルデザイン化する場合、SupporLibraryを使うことが強いられるわけですが

結構癖があるので、メモ。

バージョンは上がってるけどメンテ基準がなんか違う気がするんですよね*1

バーガーメニュー等の使い分けでハマった事(AppCompat)

ActionBarActivityを継承したクラスを使っていて

  • バック矢印のみ
  • バーガーメニュー有り

の2つの表示切り分けがある場合

バック矢印のみ

android.support.v7.app.ActionBar actionBar = getSupportActionBar();
actionBar.setHomeButtonEnabled(true);
@Override  
public boolean onOptionsItemSelected(MenuItem item) {  
      
    switch(item.getItemId()) {  
        case android.R.id.home:  
            finish();  
            return true;  
    }  
    return super.onOptionsItemSelected(item);
}

バーガーメニュー

final DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.app_name, R.string.app_name);
mDrawerToggle.setDrawerIndicatorEnabled(true);
drawerLayout.setDrawerListener(mDrawerToggle);

getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);

mDrawerToggle.syncState();//★

★の呼び出しが必要。

継承クラスの使いまわし等しなければ、下記の記述を書けばOKなんですけどね。

バック矢印のみの場合は、mDrawerToggleがnullのはずだから、ヌルポになってしまうという。。。

継承クラスが便利な時

  • でも紹介されている、 ActivityLifecycleCallbacksBackportを使いたい場合。

何でこの継承が便利かというと

  1. NotificationからのPendingIntentでActivityを起動する場合、Singletonが無理
    1. Notification =>
    2. IntentService =>
    3. Application =>
    4. startActivity な形がベター
  2. ただ最新のandroid.jar だと SINGLE_TOP等を指定しても動かない
    1. ActivityGroupのような仕組みを作る必要があり *2
    2. ActivityLifecycleCallbacksのタイミングでActivityのスタック情報を持つと楽
    3. TOPにしたい場合は、自分と同じアクティビティをfinishさせてから起動の形にすれば期待通りに動く

これが2系でも実現可能と。

でもまあ

ActivityLifecycleCallbacksを普通に使えばいいじゃん という話もあり

G様の4系端末以下の捨てっぷりは激しいものがありますが。。


AppCompatをつかうとダイアログの文字が見えなくなる時がある

背景が白地で、文字も白地になってしまうケース。

  • NG
AlertDialog.Builder builder = new AlertDialog.Builder(context);
  • OK
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.Theme_AppCompat_Light_Dialog_Alert);
builder.setTitle("title");
builder.setMessage("message")
builder.setPositiveButton("OK", null)
builder.show();

ようはAppCompatに用意されたダイアログのテーマを指定する必要があるという話

R.style | Android Developers

にstyleが用意されているんですけど、ココらへんのスタイルを明示的に指定してやる必要がある

よくググるとスタイルのカスタムの話がよく出てくるんですが、そういうレベルではない感じでハマりますね。


AsyncTaskCompatの使いかって

AsyncTaskCompat::executeParallel を使えば

API Level11以上でも SERIAL_EXECUTOR(シーケンシャル処理)にならない

までは正しいのですが、元々

// CPU数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// threadPool最小数
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
// threadPool最大数
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
// 新しく作ったスレッドを破棄するまでの時間
private static final int KEEP_ALIVE = 1;

// 新しくスレッドを作るときに使う、threadFactory
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

// AsynkTaskが使う内部queue
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);
        
public static final Executor THREAD_POOL_EXECUTOR
    = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

なので、Zenfone2とかでも同時に5スレッド程度しか実行されないんですね。

それ以上のスレッドを(例えば10スレッド同時とか)同時実行したい場合は、ThreadPoolExecutorをその数用に作って

AsyncTask::executeOnExecutor で実行するしかないという話。

AsyncTaskLoaderとかは、

  • AsyncTaskとかだと実行中にさらに重ねて実行しようとしたときに既に実行済みエラー となる
    • restartLoader等で自分で解決しやすくする
  • Taskのキャンセルを明示的ではなくしやすくする

仕組みを提供するだけで、基本 SERIAL_EXECUTORなわけなんですよね。

ここらへんはHandler::postDelay とかの考え方と似ているかもしれない*3

参考

*1:style.xmlとか微妙な関数名のリファクタとかそっち系の方が多い気がする

*2:ActivityGroup自体はdeplicated

*3:getMainLooper() でUIThread に割り込みで投げている