主頁 > 移動端開發 > Activity常見問題

Activity常見問題

2021-06-14 07:22:52 移動端開發

Activity常見問題

通過這篇博客,我們能知道以下問題:

  1. Activity 各種情況下的生命周期
  2. 彈出 DialogActivity 生命周期有什么影響?
  3. onActivityResult() 在哪兩個生命周期之間回呼?
  4. ActivityonResume() 之后才顯示的原因是什么?
  5. 通過 Sheme 協議打開 Activity
  6. Activity 什么時候會發生重建?
  7. ActivityonCreate() 方法里寫死回圈會 ANR 嗎?

1. Activity 生命周期方法相關

生命周期的回呼方法

Activity生命周期的回呼主要有 onCreate()onRestart()onStart()onResume()onPause()onStop()onDestory() 幾個方法,

呼叫時機說明:

  • onCreate():系統創建 Activity 時觸發,用來初始化 Activity 的基本組件,
  • onRestart():當處于“已停止”狀態的 Activity 即將重啟時,系統就會呼叫此回呼,
  • onStart()Activity 將進入“已啟動”狀態,并對用戶可見,
  • onResume():系統會在 Activity 開始與用戶互動之前呼叫此回呼,此時,該 Activity 位于 Activity 堆疊的頂部,并會捕獲所有用戶輸入
  • onPause():當 Activity 失去焦點并進入“已暫停”狀態時,系統就會呼叫此回呼,
  • onStop():當 Activity 對用戶不再可見時,系統就會呼叫此回呼,
  • onDestory():系統會在銷毀 Activity 之前呼叫此回呼,此回呼是 Activity 接收的最后一個回呼,通常,實作 onDestroy() 是為了確保該 Activity 的所有資源,

onStart()onStop() 是從 Activity 是否可見來回呼的,onResume()onPause() 是從 Activity 是否位于前臺來回呼的,

在生命周期里幾種狀態:

  • Activity 的整個生命周期發生在 onCreate()onDestroy() 之間,在 onCreate() 中執行“全域”狀態設定(例如狀態布局),并在 onDestroy() 中釋放資源,

  • Activity 的可見生命周期發生在 onStart()onStop() 之間,在這段時間內Activity對用戶可見,在整個生命周期中,當 Activity 在對用戶可見和隱藏倆種狀態中交替變化時,系統會多次呼叫 onStart()onStop()

  • Activity 的前臺生命周期發生在 onResume()onPause() 之間,Activity位于其他 Activity 之前(堆疊頂位置),可與用戶互動并具有輸入焦點,但狀態改變頻繁,系統會多次呼叫 onResume()onPause(),建議做些輕量級操作,

打開Activity時兩個Activity的生命周期方法的回呼順序

當一個 Activity 啟動另一個 Activity 時,它們都會經歷生命周期轉換,第一個 Activity 停止運行并進入“已暫停”或“已停止”狀態,同時創建另一個 Activity

  • Activity A 啟動 Activity B,B ActivitylaunchModestandard 或者 B Activity 沒有可復用的實體時: A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要關閉Activity A[A 被移出堆疊])

  • Activity A 啟動 Activity B,B ActivitylaunchModesingleTop且 B Activity 已經在堆疊頂時(一些特殊情況如通知欄點擊、連點、B Activity自己打開自己),此時只有 B 頁面自己有生命周期變化:B.onPause() -> B.onNewIntent() -> B.onResume()

  • 當 B ActivitylaunchModesingleInstancesingleTask 且對應的 B Activity 有可復用的實體時:A.onPause() -> B.onNewIntent() -> B.onRestart() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要關閉Activity A[A 被移出堆疊])

  • 當 B ActivityThemeDialog 時:A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume()(注意前一個 Activity 不會回呼 onStop(),因為只有在 Activity 切到后臺(不在Activity堆疊頂)不可見才會回呼 onStop();而彈出 Dialog 主題的 Activity 時前一個頁面還是可見的,只是失去了焦點而已所以僅有 onPause() 回呼)

2. 彈出 DialogActivity 生命周期有什么影響?

**先下結論:**通過 《Android Activity——啟動程序探索(一)》 《Android Activity——啟動程序探索(二)》 《Android Activity——啟動程序探索(三)》 我們知道Activity生命周期回呼都是通過ActivityTaskManagerService(ATMS)(在Android 10之前是通過 ActivityManagerService(AMS))來回呼的,但是彈出 DialogToastPopupWindow 本質上都直接是通過 WindowManager.addView() 顯示的(沒有經過 ATMS/AMS),所以不會對生命周期有任何影響,

Dialog顯示程序原始碼決議

原始碼版本為 Android 10(Api 29),不同Android版本可能有一些差別

  1. 構造方法中獲取 WindowManager 和創建 PhoneWindow 并通過 PhoneWindowsetWindowManager() 方法關聯

     public Dialog(@NonNull Context context, @StyleRes int themeResId) {
         this(context, themeResId, true);
     }
    
     Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
     	// 獲取 WindManager,實際為實作類 WindowManagerImpl
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         // 創建 PhoneWindow 物件
     	final Window w = new PhoneWindow(mContext);
         mWindow = w;
         w.setCallback(this);
         w.setOnWindowDismissedCallback(this);
         w.setOnWindowSwipeDismissedCallback(() -> {
             if (mCancelable) {
                 cancel();
             }
         });
         w.setWindowManager(mWindowManager, null, null);
         w.setGravity(Gravity.CENTER);
    
         mListenersHandler = new ListenersHandler(this);
     }
    
  2. Dialog#show() 方法原始碼實作

     public void show() {
         mCanceled = false;
     
         if (!mCreated) {
             dispatchOnCreate(null); // 沒有創建過的話,呼叫 onCreate() 方法
         } else {
             // 創建過的話,就不在呼叫 onCreate() 回呼,只是修改配置
             final Configuration config = mContext.getResources().getConfiguration();
             mWindow.getDecorView().dispatchConfigurationChanged(config);
         }
     
         onStart(); // 呼叫 onStart() 回呼方法
         mDecor = mWindow.getDecorView(); // 通過 PhoneWindow 獲取 DecorView
     
         ... // 省略,設定默認logo、icon以及確定軟鍵盤模式等
     
         // 通過 WindowManager(實際為WindowManagerImpl物件)將DecorView增加表單上
         mWindowManager.addView(mDecor, l);
         
         ... // 設定軟鍵盤模式
     
         mShowing = true; // 修改顯示標記為 true
     
         sendShowMessage();
     }
    
     // WindowManagerImpl 的 addView() 方法
     @Override
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         applyDefaultToken(params);
         // 呼叫 WindowManagerGlobal 的 addView() 方法
         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
     }
     
     // WindowManagerGlobal 的 addView() 方法核心代碼
     public void addView(View view, ViewGroup.LayoutParams params,
             Display display, Window parentWindow) {
         
         ViewRootImpl root;
         View panelParentView = null;
     
         synchronized (mLock) {
         	// 創建 ViewRootImpl 物件
             root = new ViewRootImpl(view.getContext(), display);
             view.setLayoutParams(wparams);
             // 呼叫 ViewRootImpl 的 setView() 方法
             root.setView(view, wparams, panelParentView);
         }
     }
     
     // ViewRootImpl 的 setView() 方法核心代碼
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         synchronized (this) {
             if (mView == null) {
                 mView = view;
                 // 呼叫 requestLayout() 方法,進行布局(包括measue、layout、draw)
                 requestLayout();
                 
                 mOrigWindowType = mWindowAttributes.type;
                 mAttachInfo.mRecomputeGlobalAttributes = true;
                 collectViewAttributes();
                 // 通過呼叫 Session 的 addToDisplay() 方法
                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                         getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                         mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                         mTempInsets);
                 setFrame(mTmpFrame);
             }
         }
     }
    

ViewRootImplsetView() 方法說明:

  1. mWindowSession 是通過 WindowManagerGlobalgetWindowSession() 方法獲取到的,回傳的是通過 WindowManagerServiceopenSession() 方法創建的 Session 物件

  2. mWindow 物件為 ViewRootImpl 的 內部類 static class W extends IWindow.Stub,通過定義可以看出來,是一個 Binder 物件

  3. Session 物件的 addToDisplay() 方法

     Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState) {
          // mService 為 WindowManagerService,window 就是上面的 `ViewRootImpl` 的 內部類 `static class W extends IWindow.Stub`
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                 outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                 outInsetsState);
     }
    

通過這個方法就能夠實作客戶端與 WindowManagerService 的雙向呼叫了(應用端通過 mWindowSession 呼叫 WMS, WMS 通過 mWindow (一個 Binder 物件) 呼叫應用端)

DialogFragment原始碼決議

注意:該部分原始碼來自 support v4 包

DialogFragment 的定義

class DialogFragment extends Fragment

發現就是直接繼承了 Fragment,那么就可以肯定,Fragment 有的 DialogFragment 都有,像生命周期方法等,又因為我們可以通過 getDialog() 方法獲取到 Dialog ,所以可以肯定它包含一個了 Dialog,接著我們先看 show() 方法

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

發現就是將 DialogFragment 顯示出來,那我們接著找一下,看看 Dialog 是在哪里創建的,發現如下代碼:

@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
    mDialog = onCreateDialog(savedInstanceState);
    return (LayoutInflater) mActivity.getSystemService(
            Context.LAYOUT_INFLATER_SERVICE);
}

@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new Dialog(getActivity(), getTheme());
}

發現是在 getLayoutInflater() 方法中創建的,那么這個 getLayoutInflater() 方法又是在哪里被呼叫的呢?通過查找我們發現這個方法是在 FragmentManager 中呼叫的,呼叫完這個方法之后就會呼叫 FragmentonViewCreated() 方法;而 方法的回傳值 LayoutInflater 就是 Fragment 中回呼方法 onCreateView() 的引數,

分析到這里,我們就知道了,DialogFragment 實際上是一個 Fragment ,只是他在 onViewCreated() 方法之前通過 getLayoutInflater() 方法創建了一個 Dialog,也就是一個普通的 Fragment 中包含了一個 Dialog,那么又出現了問題,我們剛剛看了 show() 方法,發現并沒有呼叫 Dialog,show() 方法,那么 Dialog 是怎么顯示的呢?我們前面就說過,DialogFragment 實際上是一個 FragmentFragment 有的 DialogFragment 都有;我們都知道Fragment有一個生命周期方法 onStart() 是在用戶可見時被呼叫的,那么 Dialog.show() 方法是否在該方法種呼叫了,看一下代碼:

@Override
public void onStart() {
    super.onStart();
    if (mDialog != null) {
        mViewDestroyed = false;
        mDialog.show();
    }
}

果然,在 Fragment 的生命周期方法 onStart()回呼中,呼叫了 Dialog.show() 方法,那么當我么呼叫 DialogFragmentshow() 方法時,把將 DialogFragment 顯示出來了,那么 Fragment 的生命周期方法 onStart() 自然會被回呼,Dialog 自然也就顯示出來了,Fragment 的生命周期方法回呼并非由 ActivityManagerService(AMS)【Android 10 以上 ActivityTaskManagerService(ATMS)】來控制,而是通過 FragmentManager 回掉的,也不會影響到 Activity 的生命周期,

3. onActivityResult 在哪兩個生命周期之間回呼?

onActivityResult 不屬于 Activity 的生命周期,但是答案很簡單,因為在 onActivityResult() 方法的注釋中就寫著答案:「You will receive this call immediately before onResume() when your activity is re-starting.」 所以 onActivityResult() 回呼先于該 Activity 的所有生命周期回呼,從 B Activity 回傳 A Activity 的生命周期呼叫為:

B.onPause() -> A.onActivityResult() -> A.onRestart() -> A.onStart() -> A.onResume()

4. Activity 在 onResume 之后才顯示的原因是什么?

通過《Android自定義View之Activity頁面的組成》我們知道了在 Activity 中呼叫 setContentView() 方法會將我們的 View 添加到 DecorView 中,DecorView 也是一個控制元件,他要加載到界面上讓用戶可見,是通過 Window (也就是 PhoneWindow)來完成的,所以我們只要知道 PhoneWindow 是在什么時候將 DecorView 加載出來的,就知道了用戶是在什么時候才可見布局資訊,那么,到底是什么時候開始將 DecorView 添加到 PhoneWindow 中的了, 在 《Android Activity——啟動程序探索(二)》 中,我們說到了 Activity 的回呼 onResume() 方法,中間有一段是會呼叫 ActivityThread#handleResumeActivity() 方法,我們來看一下這個方法的原始碼(省略大部分代碼之后的核心代碼):

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
	// 回呼 onResume() 方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
   
    final Activity a = r.activity;
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow(); // 獲取PhoneWindow并賦值
        View decor = r.window.getDecorView(); // 獲取 DecorView 控制元件
        decor.setVisibility(View.INVISIBLE);
		// 獲取ViewManager,也就是WindowManager(因為interface WindowManager extends ViewManager)
        ViewManager wm = a.getWindowManager(); 
        
        a.mWindowAdded = true;
        wm.addView(decor, l); // 關鍵點
    }
}

方法說明:

  1. ViewManager wm = a.getWindowManager() 方法最侄訓傳的是 WindowManagerImpl 物件,原因如下:

    1. Activity.getWindowManager() 方法回傳 Activity 的成員變數 mWindowManager
    2. Activity 的成員變數 mWindowManagerActivityattach() 方法中被賦值 mWindowManager = mWindow.getWindowManager(),呼叫的是 WindowgetWindowManager() 方法
    3. WindowgetWindowManager() 方法回傳的是他的成員變數 mWindowManager
    4. Window 的成員變數 mWindowManager 在他的 setWindowManager() 方法中被賦值 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this) ,所以得出結論 a.getWindowManager() 方法最侄訓傳的是 WindowManagerImpl 物件
  2. 關鍵點 wm.addView(decor, l) 呼叫的就是 WindowManagerImpl#addView() 方法了,之后的呼叫程序在當前文章前面【Dialog#show() 方法原始碼實作】 部分已經說過了,就不在重復了,

通過以上分析,我們就知道了實際上 Activity 中的布局內容是在 onResume() 回呼之后才掛載到 Window 上的,也就是這個時候才是對用戶可見的,

5. 通過 URL Sheme 協議打開 Activity

URL Scheme 是一種頁面內跳轉協議,通過這個協議可以比較方便的跳轉到app某一個頁面,也可以通過 h5 打開App頁面,

URL Sheme 的格式:[scheme]😕/[host][:port]/[path]?[query]

  • scheme:協議名稱 [scheme]😕/ 為必填,其他為非必填
  • host:主機名(域名)
  • port:埠號
  • path:路徑
  • query:引數(多個引數之間用 & 分割,類似 get 請求)

使用

1. APP端需要能通過 sheme 打開的 Activity 進行配置 intent 過濾器(在清單檔案中配置)

	<activity android:name=".TestActivity">
	    <!--Android 接收外部跳轉過濾器-->
	    <!--要想在別的App上能成功調起App,必須添加intent過濾器-->
	    <intent-filter>
	        <!-- 協議部分配置 ,注意需要跟web配置相同-->
	        <data
	            android:host="action.test.activity"
	            android:pathPrefix="/test"
	            android:port="999"
	            android:scheme="testapp" />
	
	        <!--下面這幾行也必須得設定-->
	        <action android:name="android.intent.action.VIEW" />
	
	        <category android:name="android.intent.category.DEFAULT" />
	        <category android:name="android.intent.category.BROWSABLE" />
	    </intent-filter>
	</activity>

data屬性說明:

  • scheme: 協議名稱,必填,其他為非必填(由開發人員自定義)

  • host: 域名

  • port:埠

  • path: 完整的路徑,如:http://example.com/blog/abc.html,這里將 path 設定為 /blog/abc.html 才能夠進行匹配

  • pathPrefix:路徑的開頭部分,拿上來的 Uri 來說,這里將 pathPrefix 設定為 /blog 就能進行匹配了

  • pathPattern:用運算式來匹配整個路徑,匹配符如下:

    • ” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
    • “.” 用來匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
    • 因此 “.*” 就是用來匹配任意字符0次或更多

2. 在 Activity 獲取 sheme 資訊:

	Uri uri = getIntent().getData();
    if (uri != null) {
        // 完整的url資訊
        Log.i(TAG, "url:" + uri);

        // scheme部分
        String scheme = uri.getScheme();
        Log.i(TAG, "scheme:" + scheme);

        // host部分
        String host = uri.getHost();
        Log.i(TAG, "host:" + host);

        // port部分
        int port = uri.getPort();
        Log.i(TAG, "port:" + port);

        // 訪問路勁
        String path = uri.getPath();
        Log.i(TAG, "path:" + path);
        List<String> pathSegments = uri.getPathSegments();
        Log.i(TAG, "pathSegments:" + pathSegments);

        // Query部分
        String query = uri.getQuery();
        Log.i(TAG, "query:" + query);

        //獲取指定引數值
        String params1 = uri.getQueryParameter("params1");
        String params2 = uri.getQueryParameter("params2");
        Log.i(TAG, "params1:" + params1 + "  params2:" + params2);
    }

3. 打開 Activity 的方式

1. 通過原生代碼打開

		String uri = "testapp://action.test.activity:999/test?params1=1&params2=true";
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
        startActivity(intent);

1. 通過h5代碼打開(使用瀏覽器打開包含如下代碼的 html 檔案)

		<html>
			<title>測驗</title>
		
			<body>
				 <a href="testapp://action.test.activity:999/test?params1=1&params2=true">打開APP</a>
			</body>
		</html>

6. Activity 什么時候會發生重建?

發生時機:

  1. 系統資源回收:記憶體不足,低優先級的Activity會被殺死
  2. 配置發生變化:當系統配置發生變化時,比如螢屏方向、語言的改變

橫豎屏切換不銷毀重建的方式

在清單檔案中為該 Activity 配置 android:configChanges 屬性,屬性值 orientation|screenSize(Api級別為13以下時只需要配置 orientation 即可) 對應著旋轉螢屏,locale 對應著語言變化,如此,在配置發生變化時,不會導致重建,而是走 onConfigurationChanged() 回呼方法,

保存和恢復住狀態

保存和恢復狀態分別通過 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法實作,

  1. ActivityonSaveInstanceState() 執行在 onStop() 之前,與 onPause() 沒有固定的時序關系;onRestoreInstanceState() 執行在 onStart() 之后,不止 Activity 有這兩個方法,每個 View 也有這兩個方法,用來保存 View 的資訊,
  2. 正常情況下的活動銷毀并不會呼叫這兩個方法,只有當活動例外銷毀并且有機會重現展示的時候才會進行呼叫,
  3. ActivityonRestoreInstanceState()onCreate() 都可以進行資料恢復作業,但建議采用在 onRestoreInstanceState() 中去恢復(因為呼叫了這個方法,用來存取資料的 Bundle 肯定不為 null),
  4. onSaveInstanceState()onRestoreInstanceState() 這兩個方法中,系統會默認為我們進行一定的恢復作業,比如:會為布局中的每個 View 呼叫相應的 onSaveInstanceState() 方法,讓每個視圖都能提供有關自身的應保存資訊,

具體呼叫時機說明

Activity#onSaveInstanceState() 方法呼叫時機:

  1. 當用戶按下HOME鍵時
  2. 從最近應用中選擇運行其他的程式時
  3. 按下電源按鍵(關閉螢屏顯示)時
  4. 從當前activity啟動一個新的activity時
  5. 螢屏方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會呼叫)
  6. 被系統回收時

Activity#onRestoreInstanceState() 方法呼叫時機:

  1. 被系統回收且重建時
  2. 螢屏方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會呼叫)
  3. 語言切換時

7. 在 Activity 的 onCreate() 方法里寫死回圈會 ANR 嗎?

ANR全稱:Application Not Responding,也就是應用程式無回應

Android 中產生 ANR 的原因:

  1. Service TimeOut: service 未在規定時間執行完成:前臺服務 20s,后臺 200s
  2. BroadCastQueue TimeOut: 未在規定時間內未處理完廣播:前臺廣播 10s 內, 后臺 60s 內
  3. ContentProvider TimeOut: publish 在 10s 內沒有完成
  4. Input Dispatching timeout: 5s 內未回應鍵盤輸入、觸摸螢屏等事件

根據以上 ANR 產生的原因,說明在 ActivityonCreate() 方法中寫死回圈是不會發生ANR的,但是主執行緒被死回圈一直占用了,所以當再有其他事件產生時,就不能及時回應了,從而導致ANR發生,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/287172.html

標籤:其他

上一篇:Android開發最近新聞和功能都在這里了面試必備

下一篇:Android中使用RequestQueue佇列訪問web服務器和Listview自定義配接器制作智慧醫療界面

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more