Learn to Live and Live to Learn

IT(たまにビジネス)に関する記事を読んで、考えて、使ってみたことをまとめる場。

ビルド時にDuplicate files copied

概要

以下のエラーでビルドが失敗しました.

Duplicate files copied in APK META-INF/LICENSE.txt

原因

依存ライブラリに指定した外部jarファイルを内部で展開した時に複数のjarファイルでリソースファイルなどのパスが競合してエラーになっているようです。(Qiitaより)

対応方法

該当ファイルをパッケージング時に除外することでビルドできます.
packagingOptionsにはAndroid Gradle プラグインの0.7.1以上が必要です.

${module_root}/build.gradle

android {
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
    }
}

参考

qiita.com

Android N データセーバ対応

データセーバとは

Android Nで導入された機能です.

ユーザーが [Settings] でデータセーバーを有効にし、端末が従量制課金ネットワークに接続されている場合、システムはバックグラウンドでのデータ使用をブロックし、フォアグラウンドでのデータ使用をなるべく抑えるようにアプリに指示します。
( developer.android.comより)

データ通信に制限がかかって月後半に困る人(それは私)には便利な機能かもしれませんね.
ただし,アプリ開発者側はバックグランドでしていた処理ができなくなるので対応が求められます.

データセーバ設定の確認&ホワイトリストパーミッションの要求

一番使うのはこれだと思います.データセーバが有効になっているか確認し,有効な時はホワイトリストに自アプリを登録してもらう設定画面に遷移させます.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager.isActiveNetworkMetered()
        && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        // 現在の通信が測定されているか(ダウンロードにリミットが課されているか)
        switch (connectivityManager.getRestrictBackgroundStatus()) {
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
                // データセーバ有効
                // 設定画面へ遷移
                Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS, "package:<package_name>");
                startActivity(intent);
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
                // ホワイトリストに登録されている
            case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
                // データセーバ無効
        }
    } else {
        // WiFiとかデータセーバには関係ない
    }
}

データセーバ設定の変更の監視

BroadcastReceiverを使ってデータセーバ設定の変更を検知することもできます.
ただし,AndroidManifest.xmlに書く静的な登録ではなく,registerRecieverする動的な登録でないとこのブロードキャストは受信できません.

IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED);
filter.addAction(conManager.ACTION_RESTRICT_BACKGROUND_CHANGED);
DataSaverReceiver receiver = new DataSaverReceiver();
getContext().registerReceiver(receiver, filter);
public class DataSaverReceiver extends BroadcastReceiver{
    public void onReceive(Context context, Intent intent) {
        // データセーバ設定が変更された時
    }
}

参考

developer.android.com

www.webprofessional.jp

Sources for 'Android API xx Platform' not found.

概要

Build.javaを開くとSources for 'Android API 22(1) Platform' not found.というエラーが出てきました.

インストールしても直らない🤔
(インストールされているかは,Tools > Android > SDK Manager > Show Pachage Details > Sources for Android xxがInstalledになっているかで確認できます)
Refreshを押しても反応しない🤔
(バグらしいです.泣)

という時に以下の方法で解決できたので紹介します.実行環境はAndroid2.1系と2.3系です.

解決方法

①Tools > Android > SDK Managerを開く
Android SDK LocationのEditを押す
③何もいじらず,NextをポチポチしてFinish
これでRefreshされ解決されました.

参考

kokufu.blogspot.jp

Android Web to Appする(アプリ編)

App Indexing

AndroidにはApp Indexingという仕組みがあります.

AppIndexingとは、ウェブページURLやキーワードとアプリの特定画面へのディープリンクを紐付け、Google検索結果から直接アプリの特定画面を起動させるための仕組みです。 by Qiita

ウェブもアプリも持っているサービスなんかは,この仕組みを使うと簡単にWeb to Appできます.
※ウェブからもアプリからも実装が必要なんですが今回はアプリ側の実装を紹介します.

qiita.com

設定はAndroidManifest.xmlだけで可能です.

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:scheme="https"
        android:host="m.yahoo.co.jp"
        android:path="/" />
    <data
        android:scheme="http"
        android:host="m.yahoo.co.jp"
        android:path="/" />
</intent-filter>

actionタグにはVIEW,categoryタグにはBROUSABLEDEFAULTを書きます.
DEFAULTは任意です.指定しないとGoogle検索結果以外からディープリンクできなくなります.
dataタグにはschemeなどを指定します.何が指定できるかは公式ドキュメントをご覧ください→<data> | Android Developers

今回はこんな感じ.
scheme https://m.yahoo.co.jp/
host https://m.yahoo.co.jp/
path https://m.yahoo.co.jp/

techbooster.jpn.org

起動できるかはasbコマンドでテストできます.

$ adb shell am start -a android.intent.action.VIEW -d "https://m.yahoo.co.jp/" {package name}

実装状況をテストする  |  Firebase

Android Homeキーのタップを検知する

dispatchKeyEventでいけるかなと思っていましが,これはハードキー時代の栄光のようです.

Homeキーのタップを検知する方法には

1). onUserLeaveHint
2). BroadcastReceiver

の2つあります. 1). はIntentで他アプリに遷移するときも呼ばれるので注意が必要です.

1). onUserLeaveHint

    @Override
    protected void onUserLeaveHint() {
        Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show();
    }

y-anz-m.blogspot.jp

2). BroadcastReciever

public class MainActivity extends AppCompatActivity {

    private HomeKeyReceiver mHomeKeyReceiver;

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

        // Homeキーを押された時の Reciever の登録
        mHomeKeyReceiver = new HomeKeyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        registerReceiver(mHomeKeyReceiver, filter);
    }

    private class HomeButtonReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @Override
    public void onPause(){
        super.onPause();
        unregisterReceiver(mHomeKeyReceiver);
    }

}

wada811.blogspot.com

EventBusを使って簡単にCallbackを実現する

EventBusとは

あるClassでのイベントを検知して,別のClassで何かしたい時ありませんか?

Androidには簡単にそんなCallbackを実現する仕組みがあります.それがEventBusです.

例えば,ショッピングアプリの商品ページでお気に入りボタンが押された時,未ログインならログインページに飛ばし,ログイン完了後,再び商品ページに戻してお気に入り登録処理をしたい,なんて時に使えます(^^)

今回はgreenrobotのEventBusを使いますが,EventBusの提供元は一つではないです.

こちらにはsquere社のEventBusも紹介されています. qiita.com

実装

以下のgithubに載っていますが,EventBusは3stepで実現できちゃいます. github.com

今回は「ボタンがクリックされるとMessageEventというイベントが投げられ,MainActivityにToastを表示する」アプリを実装します.

イベントを投げるActivityも受けるActivityも同じなんで有難味がないかもしれませんが,簡略化のためそうします.

①イベント定義 (MessageEvent.java)

まず,ボタンがクリックされたことを通知するイベントClassを作ります.

MessageEvent.java

public class MessageEvent {
}

②イベントを監視する準備 (MainActivity.java)

次にイベントの監視を設定します.

通知を受け取りたいクラスのonStartにEventBus.getDefault().register(this);と書き

立つ鳥跡を濁さず,onStopではEventBus.getDefault().unregister(this);をして登録解除します.

③イベントを投げる (MainActivity.java)

最後に,イベントを発火したいタイミングでEventBus#getDefault#postします.

引数は発火したいイベントクラスです.

EventBus.getDefault().post(new MessageEvent());

MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new MessageEvent());
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        Toast.makeText(this, "イベント発火", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }
}

build.gradleはこちら

compile 'org.greenrobot:eventbus:3.0.0'

簡単で便利なのでよかったら使ってみてくださーいノシ

いきなりRxAndroid

RxAndroidを使う機会があったので,調べたことをまとめました.
※理解が浅いので間違っている箇所はご指摘いただけると嬉しいです><

RxAndroidとは

Reactive Extentions Androidの略で,RxJavaを内包しAndroidで利用するための機能を追加してあります.

ソースコードはこちらです.

github.com

非同期処理が簡単に実装できます.
AsyncTaskに比べ実装が簡単で,ModelとViewで役割分担しやすいです.

使い方

Observavle:処理を実行しObserverに結果を伝える
Observer:Observavleから結果を受け取り処理する

build.gradle

compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'

MainActivity.java

package himitsu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.schedulers.Schedulers;
import rx.android.schedulers.AndroidSchedulers;

public class MainActivity extends AppCompatActivity {

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

        Observable
            .create(new Observable.OnSubscribe<Integer>() {
                @Override
                public void call(Subscriber<? super Integer> subscriber) {
                    // 重い処理
                    int i = 0;
                    Log.d("debug", "Observable:onNext1");
                    subscriber.onNext(i);
                    Log.d("debug", "Observable:onNext2");
                    Log.d("debug", "Observable:onCompleted1");
                    subscriber.onCompleted();
                    Log.d("debug", "Observable:onCompleted2");
                }
            })
            .subscribeOn(Schedulers.newThread()) // どのスレッドでObservableを走らせるか
            .observeOn(AndroidSchedulers.mainThread()) // どのスレッドでObserverを走らせるか
            .subscribe(new Observer<Integer>() { // Observable.subscribe(observer)すると処理が始まる
                @Override
                public void onCompleted() {
                    Log.d("debug", "Observer::onCompleted");
                }

                @Override
                public void onError(Throwable e) {
                    Log.d("debug", "Observer::onError");
                }

                @Override
                public void onNext(Integer i) {
                    Log.d("debug", "Observer::onNext");
                }
        });
    }
}

Android Monitor

debug: Observable:onNext1
debug: Observable:onNext2
debug: Observable:onCompleted1
debug: Observable:onCompleted2
debug: Observer::onNext
debug: Observer::onCompleted

参考

kirimin.hatenablog.com
使い方やメソッドを丁寧に解説されていて,初心者の私にも分かりやすかったです.

qiita.com
AsyncTaskとの比較が簡潔に記されていました.