ビルド時に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' } }
参考
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) { // データセーバ設定が変更された時 } }
参考
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され解決されました.
参考
Android Web to Appする(アプリ編)
App Indexing
AndroidにはApp Indexingという仕組みがあります.
AppIndexingとは、ウェブページURLやキーワードとアプリの特定画面へのディープリンクを紐付け、Google検索結果から直接アプリの特定画面を起動させるための仕組みです。 by Qiita
ウェブもアプリも持っているサービスなんかは,この仕組みを使うと簡単にWeb to Appできます.
※ウェブからもアプリからも実装が必要なんですが今回はアプリ側の実装を紹介します.
設定は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タグにはBROUSABLE
,DEFAULT
を書きます.
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/
起動できるかはasb
コマンドでテストできます.
$ adb shell am start -a android.intent.action.VIEW -d "https://m.yahoo.co.jp/" {package name}
Android Homeキーのタップを検知する
dispatchKeyEvent
でいけるかなと思っていましが,これはハードキー時代の栄光のようです.
Homeキーのタップを検知する方法には
1). onUserLeaveHint
2). BroadcastReceiver
の2つあります. 1). はIntentで他アプリに遷移するときも呼ばれるので注意が必要です.
1). onUserLeaveHint
@Override protected void onUserLeaveHint() { Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show(); }
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); } }
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で利用するための機能を追加してあります.
ソースコードはこちらです.
非同期処理が簡単に実装できます.
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との比較が簡潔に記されていました.