三星圖片旋轉(zhuǎn)
獨(dú)三星的手機(jī)拍照之后,你會(huì)很清楚的看到會(huì)把照片旋轉(zhuǎn)一下,然后你根據(jù)路徑找到的圖片就是已經(jīng)被旋轉(zhuǎn)的了。處理方式:獲取圖片的exif(Exchangeable Image File 可交換圖像文件)信息中的旋轉(zhuǎn)角度。如果被旋轉(zhuǎn)了,那么就進(jìn)行逆向選擇
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
public static Bitmap toturn(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
int width = img.getWidth();
int height = img.getHeight();
img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
return img;
}
重復(fù)創(chuàng)建快捷方式
很多手機(jī)廠商取消了快捷方式的概念,導(dǎo)致我們無(wú)法通過(guò)代碼創(chuàng)建一個(gè)我們需要的快捷方式
對(duì)于這些不支持創(chuàng)建快捷方式的手機(jī),咱就忽略了~
Android 的 Launcher 源碼在創(chuàng)建快捷方式的時(shí)候不僅會(huì)判斷 duplicate 的值,還會(huì)在數(shù)據(jù)庫(kù)中查詢一下將要被創(chuàng)建的快捷方式是否已經(jīng)存在,我們也照做就 OK 了
但是眾多廠商的url不盡相同
2.2 版本以前的URI 是:content://com.android.launcher.settings/favorites?notify=true
2.2~4.3 版本的URI 是:content://com.android.launcher2.settings/favorites?notify=true
4.4 版本以上的目前都是:content://com.android.launcher3.settings/favorites?notify=true
還有特定機(jī)型的URL也不同,這里就不列舉了~
通過(guò)權(quán)限查詢URI
ListView設(shè)置分割線與內(nèi)邊距
<?xml version="1.0" encoding="UTF-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="15dp"
android:insetRight="15dp"
android:drawable="@color/line_gray">
</inset>
<ListView
android:id="@+id/listView1"
android:divider="@drawable/list_item_divider"
android:dividerHeight="1px"
android:layout_below="@id/rlHeader1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
TextView展示小表情
//將發(fā)布的評(píng)論由Base64編碼還原為字符串,為了兼容表情符
if (data.getContent().contains("E5BE88E5B18C")) {
byte[] decodeBase64 = Base64.decodeBase64(data.getContent().replace("E5BE88E5B18C", ""));
content.setText(new String(decodeBase64));
} else {
content.setText(com.emotion.StringUtils.getEmotionContent(MomentsDetailsActivity.this, content, data.getContent()));
}
通過(guò)名稱獲取資源id
int lebId = Resources.getSystem()
.getIdentifier("permlab_accessNetworkState",
"string", "android");
String lab = getString(lebId);
WebView blocks redirect from https to http
Added in API level 21
Used with setMixedContentMode(int) In this mode, the WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. This is the least secure mode of operation for the WebView, and where possible apps should not set this mode.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
彈出軟鍵盤(pán)問(wèn)題
有個(gè)需求:比如讓你一進(jìn)入頁(yè)面就彈出軟鍵盤(pán),網(wǎng)上一搜一堆代碼,發(fā)現(xiàn)就是彈不出來(lái)。為什么?因?yàn)榭赡苣愕哪繕?biāo)view當(dāng)時(shí)還沒(méi)有完成完整繪制流程
getHandler().postDelayed(new Runnable() {
@Override
public void run() {
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0);
}
},50);
webview調(diào)用js報(bào)線程錯(cuò)誤
W/WebView(2088): Java.lang.Throwable: A WebView method was called on thread ‘JavaBridge’. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {b3dbcb18} called on Looper (JavaBridge, tid 120) {b44a1af8}, FYI main Looper is Looper (main, tid 1) {b3dbcb18})
W/WebView(2088): at android.webkit.WebView.checkThread(WebView.java:2063)
W/WebView(2088): at android.webkit.WebView.loadUrl(WebView.java:794)
W/WebView(2088): at com.ue.oa.activity.XFormActivity.alert(XFormActivity.java:180)
W/WebView(2088): at com.ue.oa.activity.XFormActivity$FormActions.save(XFormActivity.java:193)
W/WebView(2088): at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
W/WebView(2088): at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
W/WebView(2088): at android.os.Handler.dispatchMessage(Handler.java:102)
W/WebView(2088): at android.os.Looper.loop(Looper.java:136)
W/WebView(2088): at android.os.HandlerThread.run(HandlerThread.java:61)
webView.post(new Runnable() {
@Override
public void run() {
// your code
}
});
Warning Exception while processing IOException Please correct
混淆規(guī)則中加入
-ignorewarnings # 抑制警告
Activity啟動(dòng)模式和onActivityResult之間的沖突
public void startActivityForResult (Intent intent, int requestCode, Bundle options)
Added in API level 16
Launch an activity for which you would like a result when it finished. When this activity exits, your onActivityResult() method will be called with the given requestCode. Using a negative requestCode is the same as calling startActivity(Intent) (the activity is not launched as a sub-activity).
Note that this method should only be used with Intent protocols that are defined to return a result. In other protocols (such as ACTION_MAIN or ACTION_VIEW), you may not get the result when you expect. For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.
As a special case, if you call startActivityForResult() with a requestCode >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your activity, then your window will not be displayed until a result is returned back from the started activity. This is to avoid visible flickering when redirecting to another activity.
This method throws ActivityNotFoundException if there was no Activity found to run the given Intent.
如果運(yùn)行所給Intent的Activity沒(méi)被找到,該方法會(huì)拋出ActivityNotFoundException異常
解決方案
singleTop模式,可用來(lái)解決棧頂多個(gè)重復(fù)相同的Activity的問(wèn)題
singleTask模式和后面的singleInstance模式都是只在另外一個(gè)任務(wù)棧中
所以當(dāng)使用singleTask與singleInstance模式會(huì)有上述問(wèn)題
moveTaskToBack退后臺(tái)
方法:public boolean moveTaskToBack(boolean nonRoot)
activity里有這個(gè)方法,參數(shù)說(shuō)明如下:
nonRoot=false→ 僅當(dāng)activity為task根(即首個(gè)activity例如啟動(dòng)activity之類的)時(shí)才生效
nonRoot=true→ 忽略上面的限制
這個(gè)方法不會(huì)改變task中的activity中的順序,效果基本等同于home鍵
應(yīng)用場(chǎng)景:
比如有些activity諸如引導(dǎo)圖之類的,用戶在按返回鍵的時(shí)候你并不希望退出(默認(rèn)就finish了),而是只希望置后臺(tái),就可以調(diào)這個(gè)方法
解決EditText自動(dòng)獲取焦點(diǎn)問(wèn)題
問(wèn)題分析:EditText自動(dòng)獲取焦點(diǎn),導(dǎo)致進(jìn)入頁(yè)面時(shí)自動(dòng)彈出輸入法
解決方法:設(shè)置其他控件可獲取焦點(diǎn)并允許通過(guò)觸摸獲取焦點(diǎn)
<RelativeLayout
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@color/subjectColor"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true">
......
</RelativeLayout>
immutable bitmap passed to Canvas constructor
出現(xiàn)Immutable bitmap passed to Canvas constructor錯(cuò)誤的原因是如果不用copy的方法,直接引用會(huì)對(duì)資源文件進(jìn)行修改,而Android是不允許在代碼里修改res文件里的圖片
BitmapFactory.decodeResource(getResources(), R.drawable.xiao).copy(Bitmap.Config.ARGB_8888, true);
ScrollView嵌套 ListView GridView 引發(fā)的自動(dòng)滑動(dòng)問(wèn)題
ScrollView嵌套 listView gridView 引發(fā)的自動(dòng)滑動(dòng) 尤其當(dāng)listView或gridView在屏幕底部或超出屏幕時(shí)尤為明顯。一般出現(xiàn)這種情況是焦點(diǎn)問(wèn)題,這時(shí)如果不想listView或girdView獲取焦點(diǎn)的話,需要在ScrollView下的根布局設(shè)置
android:descendantFocusability=”blocksDescendants”
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants">
設(shè)置之后 就不會(huì)自動(dòng)滑動(dòng)到底部去了 我是這樣就解決的
descendantFocusability有三種屬性
beforeDescendants :viewgroup會(huì)優(yōu)先其子類控件而獲取到焦點(diǎn)
afterDescendants:viewgroup只有當(dāng)其子類控件不需要獲取焦點(diǎn)時(shí)才獲取焦點(diǎn)
blocksDescendants:viewgroup會(huì)覆蓋子類控件而直接獲得焦點(diǎn)
退出所有Activity 殺死應(yīng)用 結(jié)束應(yīng)用
1 拋出運(yùn)行時(shí)異常:使程序崩潰退出
2 System.exit(0)
以上兩種方法:過(guò)于極端,可能會(huì)導(dǎo)致后臺(tái)的服務(wù)等也隨著退出
3 android.os.Process.killProcess(Process.myPid())
4 通過(guò)ActivityManager來(lái)結(jié)束指定包名的所有界面
5 利用設(shè)置Intent的FLAG參數(shù)為FLAG_ACTIVITY_CLEAR_TOP
當(dāng)啟動(dòng)某個(gè)Activity時(shí)會(huì)清除其上方的所有Activity
6 創(chuàng)建自己的ActivityManager統(tǒng)一管理所有Activity
在BaseActivity的onCreate方法中調(diào)用添加方法
在BaseActivity的onDestroy方法中調(diào)用移除方法
拍照裁減圖片時(shí)出現(xiàn)SecurityException
你在跳轉(zhuǎn)activity的過(guò)程中攜帶的extras中有圖片的Bitmap,應(yīng)用盡量減少圖片的尺寸大小,或者通過(guò)保存圖片到sd卡中或者通過(guò)uri方式傳遞圖片參數(shù)
open failed EBUSY (Device or resource busy)
處理Android文件的時(shí)候遇到了這樣一個(gè)問(wèn)題:當(dāng)刪除一個(gè)文件后,無(wú)法再次創(chuàng)建相同名稱的文件。通過(guò)捕獲異??梢园l(fā)現(xiàn)系統(tǒng)爆出了open failed: EBUSY (Device or resource busy)的異常,大致是改文件仍在操作中,無(wú)法進(jìn)行其他操作的意思。StackOverFlow上說(shuō),是由于android系統(tǒng)的原因,導(dǎo)致刪除的時(shí)候并沒(méi)有釋放文件鎖,從而導(dǎo)致無(wú)法再次創(chuàng)建。
解決方案:先對(duì)要?jiǎng)h除的文件進(jìn)行重命名,然后再刪除。這樣刪除過(guò)程中的文件鎖就加在另一個(gè)文件上了,不會(huì)影響再次創(chuàng)建的過(guò)程。
final File to = new File(file.getAbsolutePath() + System.currentTimeMillis());
file.renameTo(to);
to.delete();
最優(yōu)單例
1.實(shí)現(xiàn)了懶漢模式,在需要的時(shí)候才創(chuàng)建
2.避免了多線程并發(fā)訪問(wèn)導(dǎo)致的問(wèn)題
3.解決了代碼重拍優(yōu)化導(dǎo)致的問(wèn)題
public class ClearViewHelper {
private void ClearViewHelper(){}
public static ClearViewHelper getInstance(){
return ClearViewHelperHolder.clearViewHelper;
}
private static class ClearViewHelperHolder{
public static ClearViewHelper clearViewHelper = new ClearViewHelper();
}
}
adapter.notifyDataSetChanged無(wú)效
問(wèn)題:adapter.notifyDataSetChanged沒(méi)有反應(yīng),要觸摸屏幕才可以改變數(shù)據(jù)
原因:數(shù)據(jù)集合可能正在被操作(增/刪/改/查)
解決:延時(shí)調(diào)用或者確保在數(shù)據(jù)穩(wěn)定后調(diào)用
如何生成一個(gè)二維碼同時(shí)支持多種支付方式
問(wèn)題:如何生成一個(gè)二維碼,同時(shí)支持多種支付方式(如:微信支付、支付寶、百度錢(qián)包等等)
分析:每個(gè)支付平臺(tái)他們所使用的都是他們自己的規(guī)范,他們之間的二維碼規(guī)范是相互獨(dú)立的。本質(zhì)上來(lái)說(shuō),這個(gè)需要是無(wú)法實(shí)現(xiàn)的,但是可以通過(guò)間接的方式
方案:生成的二維碼存放支付鏈接,掃描打開(kāi)后就是支付頁(yè)面,提供相關(guān)的支付方式給用戶選擇
Fragment調(diào)用getActivity為null的問(wèn)題
很多人都曾被這個(gè)問(wèn)題所困擾,如果app長(zhǎng)時(shí)間在后臺(tái)運(yùn)行,再次進(jìn)入app的時(shí)候可能會(huì)出現(xiàn)crash,而且fragment會(huì)有重疊現(xiàn)象。如果系統(tǒng)內(nèi)存不足、切換橫豎屏、app長(zhǎng)時(shí)間在后臺(tái)運(yùn)行,Activity都可能會(huì)被系統(tǒng)回收然后重建,但Fragment并不會(huì)隨著Activity的回收而被回收,創(chuàng)建的所有Fragment會(huì)被保存到Bundle里面,從而導(dǎo)致Fragment丟失對(duì)應(yīng)的Activity
隱式意圖的判斷問(wèn)題
問(wèn)題:當(dāng)我們使用隱式意圖的時(shí)候有可能報(bào)錯(cuò)
分析:當(dāng)我們采用隱式意圖去調(diào)用時(shí),系統(tǒng)所有會(huì)進(jìn)行意圖的匹配(包括action、category以及data),那么匹配結(jié)果是有可能匹配不到的,那么此時(shí)就會(huì)報(bào)錯(cuò)
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(null == componentName){
// Intent匹配失敗
}
PackageManager pm = this.getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if(null == resolveInfos){
// Intent匹配失敗
}
兩種方式的區(qū)別
resolveActivity方式返回的是最佳匹配的Activity
queryIntentActivities返回的是所有匹配成功的Activity
PopupWindow彈出導(dǎo)致ANR
popupWindow.setBackgroundDrawable(new BitmapDrawable());
保存圖片并且更新相冊(cè)
/**
* 保存方法
*/
public void saveBitmap() {
if (bitmap == null) {
bitmap = drawableToBitmap(image.getDrawable());
}
image.getDrawable();
File f = new File(Environment.getExternalStorageDirectory() + "/hongwu/image", System.currentTimeMillis() + ".jpg");
if (f.exists()) {
f.delete();
}
try {
FileOutputStream out = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Toast.makeText(this, "圖片已保存至/storage/image/ 文件夾", Toast.LENGTH_SHORT).show();
UpdateAlbum(EaseShowBigImageActivity.this, f.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void UpdateAlbum(Context context, String saveImagePath) {
int androidVersion = Build.VERSION.SDK_INT;
if (androidVersion >= 19) {
MediaScannerConnection.scanFile(context,
new String[]{saveImagePath}, null,
new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
}
});
} else {
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://" + saveImagePath)));
}
}
/**
* Drawable轉(zhuǎn)化為Bitmap
*/
public static Bitmap drawableToBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;
}
finish() onDestroy() 和System.exit()的區(qū)別
Activity.finish()
Call this when your activity is done and should be closed.
在你的activity動(dòng)作完成的時(shí)候,或者Activity需要關(guān)閉的時(shí)候,調(diào)用此方法。
當(dāng)你調(diào)用此方法的時(shí)候,系統(tǒng)只是將最上面的Activity移出了棧,并沒(méi)有及時(shí)的調(diào)用onDestory()方法,其占用的資源也沒(méi)有被及時(shí)釋放。因?yàn)橐瞥隽藯?,所以?dāng)你點(diǎn)擊手機(jī)上面的“back”按鍵的時(shí)候,也不會(huì)再找到這個(gè)Activity。
Activity.onDestory()
the system is temporarily destroying this instance of the activity to save space.
系統(tǒng)銷(xiāo)毀了這個(gè)Activity的實(shí)例在內(nèi)存中占據(jù)的空間。
在Activity的生命周期中,onDestory()方法是他生命的最后一步,資源空間等就被回收了。當(dāng)重新進(jìn)入此Activity的時(shí)候,必須重新創(chuàng)建,執(zhí)行onCreate()方法。
System.exit(0)
這玩意是退出整個(gè)應(yīng)用程序的,是針對(duì)整個(gè)Application的。將整個(gè)進(jìn)程直接KO掉。
commitAllowingStateLoss
你在onSaveInstanceState()之后調(diào)用FragmentTransaction#commit()的時(shí)候,transation不會(huì)被記錄。因?yàn)樗粫?huì)作為之前Activity的狀態(tài)被保存。從用戶的角度來(lái)說(shuō),這個(gè)transaction就像丟失了,導(dǎo)致UI狀態(tài)意外的丟失。為了保證用戶體驗(yàn),Android不計(jì)一切代價(jià)避免狀態(tài)丟失,也就是當(dāng)它發(fā)生的時(shí)候簡(jiǎn)單地拋出一個(gè)IllegalStateException
可使用commitAllowingStateLoss()作為解決方案
關(guān)于獲取當(dāng)前Activity
在Android開(kāi)發(fā)過(guò)程中,我們有時(shí)候需要獲取當(dāng)前的Activity實(shí)例,比如彈出Dialog操作,必須要用到這個(gè)。關(guān)于如何實(shí)現(xiàn)由很多種思路,這其中有的簡(jiǎn)單,有的復(fù)雜,這里簡(jiǎn)單總結(jié)一下
反射
反射是我們經(jīng)常會(huì)想到的方法,思路大概為
1 獲取ActivityThread中所有的ActivityRecord
2 從ActivityRecord中獲取狀態(tài)不是pause的Activity并返回
然而這種方法并不是很推薦,主要是有以下的不足:
- 反射通常會(huì)比較慢
- 不穩(wěn)定性:這個(gè)才是不推薦的原因,Android框架代碼存在修改的可能性,無(wú)法保證mActivities,paused固定不變。所以可靠性不是完全可靠
Activity基類
既然反射不是很可靠,那么有一種比較可靠的方式,就是使用Activity基類
在BaseActivity的onResume方法中,將當(dāng)前Activity實(shí)例保存到一個(gè)變量中
public class BaseActivity extends Activity{@OverrideprotectedvoidonResume(){
super.onResume();
MyActivityManager.getInstance().setCurrentActivity(this);
}
}
然而,這一種方法也不是完美的,因?yàn)檫@種方法是基于約定的;要求每個(gè)Activity都必須繼承BaseActivity,若出現(xiàn)非繼承BaseActivity的就可能有問(wèn)題
回調(diào)方法
Android 自 API 14 開(kāi)始引入了一個(gè)方法
即Application的registerActivityLifecycleCallbacks方法
用來(lái)監(jiān)聽(tīng)所有Activity的生命周期回調(diào)
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { }
@Override
public void onActivityStarted(Activity activity) { }
@Override
public void onActivityResumed(Activity activity) {
MyActivityManager.getInstance().setCurrentActivity(activity); }
@Override
public void onActivityPaused(Activity activity) { }
@Override
public void onActivityStopped(Activity activity) { }
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { }
@Override
public void onActivityDestroyed(Activity activity) { }
});
}
}
public class MyActivityManager {
private static MyActivityManager mManager;
private WeakReference<Activity> mReference;
public MyActivityManager() {
}
public static MyActivityManager getInstance() {
if (mManager == null)
synchronized (String.class) {
if (mManager == null)
mManager = new MyActivityManager();
}
return mManager;
}
public void setCurrentActivity(Activity activity) {
mReference = new WeakReference<Activity>(activity);
}
public Activity getCurrentActivity(Activity activity) {
if (mReference != null)
if (mReference.get() != null)
return mReference.get();
return null;
}
}
那么為什么要使用弱引用持有Activity實(shí)例呢?
其實(shí)最主要的目的就是避免內(nèi)存泄露,因?yàn)槭褂媚J(rèn)的強(qiáng)引用會(huì)導(dǎo)致Activity實(shí)例無(wú)法釋放,導(dǎo)致內(nèi)存泄露的出現(xiàn)
跳轉(zhuǎn)目標(biāo)頁(yè)面并清除之上頁(yè)面
業(yè)務(wù)需求:從 A -> B -> C -> D頁(yè)面后從D頁(yè)面跳轉(zhuǎn)到A頁(yè)面并清除BCD頁(yè)面
解決方式:
1 設(shè)置Intent的flags為FLAG_ACTIVITY_CLEAR_TOP
2 設(shè)置目標(biāo)頁(yè)面(即A)的launchMode為singleTask
作用:當(dāng)目標(biāo)界面在棧中已存在時(shí)則復(fù)用并清除在它之上的所有Activity
關(guān)于Intent.makeRestartActivityTask
問(wèn)題:使用 PendingIntent 如何進(jìn)入多層頁(yè)面
一般來(lái)講,點(diǎn)擊一個(gè)notification后,都會(huì)打開(kāi)一個(gè)Activity做為對(duì)點(diǎn)擊事件的響應(yīng),這個(gè)Activity是之前在PendingIntent中設(shè)置好的
經(jīng)常玩Android手機(jī)的應(yīng)該都有印象,在日歷應(yīng)用中,你新建一個(gè)提醒,當(dāng)提醒通知收到后,你點(diǎn)擊通知,會(huì)進(jìn)入提醒的內(nèi)容頁(yè)面,如果這個(gè)時(shí)候按back鍵,會(huì)直接退出應(yīng)用
但是在Gmail的應(yīng)用中,如果有一封新郵件到來(lái),那么點(diǎn)擊通知后,會(huì)進(jìn)入到郵件的內(nèi)容頁(yè)面,等你看完郵件,點(diǎn)擊back鍵,會(huì)退到郵件列表頁(yè)面,再按back鍵,才會(huì)退出應(yīng)用
第一種情況:
點(diǎn)擊Notification ——>進(jìn)入SubActivity ——> back鍵 ——> 退出應(yīng)用
第二種情況:
點(diǎn)擊Notification ——>進(jìn)入SubActivity ——> back鍵 ——> 退到ParentActivity ——>back鍵 ——>退出應(yīng)用
第一種情況比較簡(jiǎn)單,只需要在PendingIntent中指定Activity
PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
第二種情況 PendingIntent.getActivities + makeRestartActivityTask
第二種情況稍微復(fù)雜,因?yàn)槿绻淮蜷_(kāi)一個(gè)SubActivity,程序沒(méi)辦法知道它的上一級(jí)Activity是誰(shuí),所以需要在點(diǎn)擊Notification時(shí)打開(kāi)一組Activity
PendingIntent提供了個(gè)靜態(tài)方法getActivities,里面可以設(shè)置一個(gè)Intent數(shù)組,用來(lái)指定一系列的Activity
PendingIntent pendingIntent = PendingIntent.getActivities(this, 0x6666, makeIntentStack(this), PendingIntent.FLAG_CANCEL_CURRENT);
mManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 35000, pendingIntent);
private Intent[] makeIntentStack(Context context) {
Intent[] intents = new Intent[4];
// makeRestartActivityTask 用來(lái)重啟應(yīng)用程序的任務(wù)棧
intents[0] = Intent.makeRestartActivityTask(new ComponentName(context, MainActivity.class));
intents[1] = new Intent(context, Main2Activity.class);
intents[2] = new Intent(context, Main3Activity.class);
intents[3] = new Intent(context, Main4Activity.class);
return intents;
}
獨(dú)立進(jìn)程導(dǎo)致Application實(shí)例多次
獨(dú)立進(jìn)程會(huì)導(dǎo)致Application多次實(shí)例化
執(zhí)行多次onCreate,導(dǎo)致相關(guān)資源重復(fù)初始化
解決方式:通過(guò)判斷當(dāng)前進(jìn)程名稱有選擇地進(jìn)行相關(guān)操作
public void onCreate() {
super.onCreate();
String nowProcessName = ProcessUtil.getProcessName(this, Process.myPid());
if (SERVICE_PROCESS_NAME.equals(nowProcessName)) {
init();
}
}
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
xml布局中的權(quán)重問(wèn)題
android:layout_weight=”3”
權(quán)重 weight 的取值含義:占據(jù)剩余空間的比例
注意:權(quán)重值越大,渲染優(yōu)先級(jí)越低(后渲染)
權(quán)重值越小,渲染優(yōu)先級(jí)越高(先渲染)
可滾動(dòng)的大圖ImageView
思路:HorizontalScrollView + 大尺寸ImageView
解析:滾動(dòng)View中放置相對(duì)布局,在相對(duì)布局中又放置大尺寸的ImageView,實(shí)現(xiàn)可滾動(dòng)的大圖ImageView
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="1024dp"
android:layout_height="match_parent"
android:src="@drawable/pic"/>
</RelativeLayout>
</HorizontalScrollView>
Fragment嵌套使用存在的問(wèn)題
問(wèn)題1:當(dāng)使用Fragment去嵌套另外一些子Fragment的時(shí)候,我們需要去管理子Fragment,這時(shí)候需要調(diào)用ChildFragmentManager去管理這些子Fragment,由此可能產(chǎn)生的Exception主要是:
java.lang.IllegalStateException: No activity
首先我們來(lái)分析一下Exception出現(xiàn)的原因:
通過(guò)DEBUG發(fā)現(xiàn),當(dāng)?shù)谝淮螐囊粋€(gè)Activity啟動(dòng)Fragment,然后再去啟動(dòng)子Fragment的時(shí)候,存在指向Activity的變量,但當(dāng)退出這些Fragment之后回到Activity,然后再進(jìn)入Fragment的時(shí)候,這個(gè)變量變成null,這就很容易明了為什么拋出的異常是No activity
這個(gè)Exception是由什么原因造成的呢?如果想知道造成異常的原因,那就必須去看Fragment的相關(guān)代碼,發(fā)現(xiàn)Fragment在detached之后都會(huì)被reset掉,但是它并沒(méi)有對(duì)ChildFragmentManager做reset,所以會(huì)造成ChildFragmentManager的狀態(tài)錯(cuò)誤
找到異常出現(xiàn)的原因后就可以很容易的去解決問(wèn)題了,我們需要在Fragment被detached的時(shí)候去重置ChildFragmentManager,即:
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class
.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
問(wèn)題2:當(dāng)我們從一個(gè)Activity啟動(dòng)了一個(gè)Fragment,然后在這個(gè)Fragment中又去實(shí)例化了一些子Fragment,在子Fragment中去有返回的啟動(dòng)了另外一個(gè)Activity,即通過(guò)startActivityForResult方式去啟動(dòng),這時(shí)候造成的現(xiàn)象會(huì)是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式啟動(dòng),那么只有Activity會(huì)接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式啟動(dòng),那么只有父Fragment能接收(此時(shí)Activity也能接收),但無(wú)論如何子Fragment接收不到OnActivityResult
解決方式:
方式1:使用getActivity的startActivityForResult方法去啟動(dòng)
在Activity的onActivityResult方法中調(diào)用相應(yīng)父fragment的onActivityResult方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " activity");
mFragment.onActivityResult(requestCode,resultCode,data);
}
在父fragment的onActivityResult方法中調(diào)用相應(yīng)子fragment的onActivityResult方法
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " father");
mFragmentChild.onActivityResult(requestCode,resultCode,data);
}
onActivityResult順序:Activity -> 父fragment -> 子fragment
方式2:使用getParentFragment的startActivityForResult方法去啟動(dòng)
在父fragment的onActivityResult方法中調(diào)用相應(yīng)子fragment的onActivityResult方法
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " father");
mFragmentChild.onActivityResult(requestCode,resultCode,data);
}
onActivityResult順序:父fragment -> 子fragment -> Activity
點(diǎn)擊兩次才觸發(fā)點(diǎn)擊事件
問(wèn)題案例:使用TextView作為接收點(diǎn)擊事件的控件
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
那么此時(shí)這樣設(shè)置的結(jié)果是:
1 TextView 可點(diǎn)擊
2 TextView 可獲取焦點(diǎn)
3 TextView 可通過(guò)觸摸獲取焦點(diǎn)
問(wèn)題:需要點(diǎn)擊兩次才能夠觸發(fā)點(diǎn)擊事件
第一下點(diǎn)擊:從其他地方獲取焦點(diǎn)
第二次點(diǎn)擊:觸發(fā)點(diǎn)擊事件
解決:
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
或者
android:clickable="true"
android:focusable="false"
界面展開(kāi)時(shí)會(huì)白一下問(wèn)題
當(dāng)Activity首次啟動(dòng)時(shí)應(yīng)用界面會(huì)白的閃一下影響用戶體驗(yàn)
可在style.xml文件中添加item設(shè)置默認(rèn)的Window背景
<item name="android:windowBackground">@drawable/bg</item>
移除控件/按鈕背景
移除控件背景(如:EditText)
android:background="@null"
移除按鈕背景(如:RadioButton)
android:button="@null"
Android內(nèi)存泄漏之Handle
/**
* 當(dāng)Android應(yīng)用程序啟動(dòng)時(shí),會(huì)在主線程中創(chuàng)建一個(gè)Looper對(duì)象
* 該Looper對(duì)象存在于應(yīng)用程序的整個(gè)生命周期中
* Looper對(duì)象實(shí)現(xiàn)了簡(jiǎn)單的消息隊(duì)列,按一定的順序發(fā)送消息
* 給Handle對(duì)象,由Handle對(duì)象進(jìn)行消息處理
* 當(dāng)創(chuàng)建Handle對(duì)象時(shí)會(huì)與Looper對(duì)象進(jìn)行關(guān)聯(lián)
* 在消息發(fā)送后,Message對(duì)象持有發(fā)送該消息的Handle對(duì)象
* 從而調(diào)用該Handle對(duì)象的handleMessage方法處理消息
*
* 如何造成內(nèi)存泄漏?
* 首先,所有內(nèi)部類(包括匿名內(nèi)部類,不包括靜態(tài)內(nèi)部類)都會(huì)
* 持有其外部類的引用
* 此時(shí)外部類是Activity,那么就會(huì)造成內(nèi)存泄漏
* Message對(duì)象持有Handle對(duì)象的引用
* 而Handle對(duì)象是一個(gè)匿名內(nèi)部類對(duì)象
* 持有其外部類Activity的引用
* 此處延時(shí)20s發(fā)送了一個(gè)空消息
* 在Activity結(jié)束后消息仍然沒(méi)有被處理完畢
* 此時(shí)Message對(duì)象持有Handle對(duì)象的引用
* 而Handle對(duì)象又持有Activity的引用
* 因此此時(shí)GC無(wú)法釋放Activity對(duì)象,導(dǎo)致內(nèi)存泄漏
*
* 如何避免?
* 1 使Handle對(duì)象不會(huì)持有外部類的引用 1:靜態(tài)內(nèi)部類 2:外部類
* 2 創(chuàng)建弱引用(使Handle對(duì)象不持有Activity的強(qiáng)引用) WeakReference
* 解析:使用靜態(tài)內(nèi)部類/外部類來(lái)自定義Handler派生類
* 這樣不會(huì)導(dǎo)致Handle對(duì)象持有外部類對(duì)象的引用
* 但是靜態(tài)內(nèi)部類/外部類無(wú)法直接操作Activity中的變量
* 因此在實(shí)例化Handler對(duì)象時(shí)需要傳入Activity對(duì)象以便在Handler中進(jìn)行相關(guān)操作
* 此時(shí)雖然Handler對(duì)象不會(huì)持有外部類引用,但是將Activity對(duì)象作為參數(shù)傳入后
* 仍然會(huì)導(dǎo)致Handler對(duì)象持有Activity對(duì)象的引用
* 那么此時(shí)就需要為Activity對(duì)象創(chuàng)建弱引用:GC對(duì)弱引用是采用急切回收方式
* 即當(dāng)只有弱引用指向?qū)ο髸r(shí),那么垃圾回收器會(huì)立即回收該對(duì)象
*
* 仍然存在的問(wèn)題?
* 在Activity結(jié)束后,Handler 對(duì)象還在繼續(xù)處理消息隊(duì)列中的Message
* 正常在Activity結(jié)束后,消息已經(jīng)沒(méi)有處理的必要了
* 那么可以在onStop/onDestroy方法中移除消息對(duì)象
* removeMessages 移除Message
* removeCallbacks 移除Runnable
* removeCallbacksAndMessages 移除指定/所有(當(dāng)參數(shù)為null時(shí)移除所有)
*/
public class MainActivity extends AppCompatActivity {
private final int WHAT_ONE = 0x3333;
private final int WHAT_TWO = 0x1233;
private final int WHAT_THREE = 0x9231;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(this);
mHandler.sendEmptyMessageDelayed(WHAT_ONE, 20000);
mHandler.sendEmptyMessageDelayed(WHAT_TWO, 20000);
mHandler.sendEmptyMessageDelayed(WHAT_THREE, 20000);
this.finish();
}
// 使用靜態(tài)內(nèi)部類(相當(dāng)于外部類):該類對(duì)象不會(huì)持有外部類的引用
private static class MyHandler extends Handler {
// 聲明弱引用用于存放Activity
private WeakReference<MainActivity> mReference;
public MyHandler(MainActivity activity) {
// 實(shí)例化弱引用對(duì)象
mReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 獲取所引用的對(duì)象
if (mReference.get() != null) {
mReference.get().todo();
}
}
}
public void todo() {
System.out.println("todo something...");
}
@Override
protected void onDestroy() {
// 移除回調(diào)對(duì)象 Runnable接口對(duì)象
// mHandler.removeCallbacks(r);
// 移除消息對(duì)象 Message
mHandler.removeMessages(WHAT_ONE);
mHandler.removeMessages(WHAT_TWO);
mHandler.removeMessages(WHAT_THREE);
// 或者使用該方法移除指定/所有 (當(dāng)參數(shù)為null時(shí)則移除所有)
// mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}