導航:首頁 > 操作系統 > android子線程中更新ui

android子線程中更新ui

發布時間:2023-05-25 10:21:25

A. 在多線程中,子線程更新主線程ui有哪些方法及注意點

android

UI多線程Androidthread工作

在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認的情況下,所有這個程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的兩種,除此之外還有Content Provider和Broadcast Receiver)都會跑在這個Process。

一個Android 程序默認情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程序運行的時候就被創建,是一個Process當中的主線程Main Thread,主要是負責控制UI界面的顯示、更新和控制項交互。在Android程序創建之初,一個Process呈現的是單線程模型,所有的任務都在一個線程中運行。因此,我們認為,UI Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載數據,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程。

那麼,UI Thread如何和其他Thread一起工作呢?常用方法是:

誕生一個主線程的Handler物件,當做Listener去讓子線程能將訊息Push到主線程的Message Quene里,以便觸發主線程的handlerMessage()函數,讓主線程知道子線程的狀態,並在主線程更新UI。

例如,在子線程的狀態發生變化時,我們需要更新UI。如果在子線程中直接更新UI,通常會拋出下面的異常:11-07 13:33:04.393: ERROR/javaBinder(1029):android.view.ViewRoot$:Only the original thread that created a view hierarchy can touch its views.

意思是,無法在子線程中更新UI。為此,我們需要通過Handler物件,通知主線程Ui Thread來更新界面。

如下,首先創建一個Handler,來監聽Message的事件:

private final int UPDATE_UI = 1;private Handler mHandler = new MainHandler();private class MainHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case UPDATE_UI: {Log.i("TTSDeamon", "UPDATE_UI");showTextView.setText(editText.getText().toString());ShowAnimation();break;}default:break;}}}

或者

private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case UPDATE_UI: {Log.i("TTSDeamon", "UPDATE_UI");showTextView.setText(editText.getText().toString());ShowAnimation();break;}default:break;}}}

當子線程的狀態發生變化,則在子線程中發出Message,通知更新UI。

mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);

在我們的程序中,很多Callback方法有時候並不是運行在主線程當中的,所以如果在Callback方法中更新UI失敗,也可以採用上面的方法。

B. android里如何在子線程中如何更新主線程的控制項

步驟:
1、創建Handler對象(此處創建於主線程中便於更新UI)。
2、構建Runnable對象,在Runnable中更新界面。
3、在子線程的run方法中向UI線程post,runnable對象來更新UI。
代碼
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;

import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button button;
private TextView textview;
private final int SPLASH_DISPLAY_LENGHT = 1;
private static int flag = 0;
private static int count=0;
private int ab=1;
private boolean isrun = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button1);
textview = (TextView)findViewById(R.id.textView1);
final Handler handler= new Handler();

final Runnable runnable = new Runnable() {
public void run() {
textview.setText(Integer.toString(ab));
}
};

final Thread t = new Thread(){
//public boolean isrun=true;
@Override
public void run() {
while(isrun)
{
handler.post(runnable); //加入到消息隊列 這樣沒有啟動新的線程,雖然沒有報異常。但仍然阻塞ProgressDialog的顯示
ab++;
try {
sleep(1000); //直接調用
} catch (InterruptedException e) {
return;
}
}
}
};
t.start();
button.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View view)
{
isrun=false;
}
});

}

}

C. 為什麼loop之後就可以子線程更新ui

我們常常聽到這么一句話:更新UI要在UI線程(或者說主線程)中去更新,不要在子線程中更新UI,而Android官方也建議我們不要在非UI線程直接更新UI。

事實是不是如此呢,做一個實驗:

更新之前:

代碼:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});

thread.start();
}

@Override
protected void onDestroy() {
super.onDestroy();
}
}
登錄後復制

這里在Activity裡面新建了一個子線程去更新UI,按理說會報錯啊,可是執行結果是並沒有報錯,如圖所示:

接下來讓線程休眠一下:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {

try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

textView.setText("text text text");
}
});

thread.start();
}

}
登錄後復制

應用報錯,拋出異常:

android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views

只有創建View層次結構的線程才能修改View,我們在非UI主線程裡面更新了View,所以會報錯

因為在OnCreate裡面睡眠了一下才報錯,這是為什麼呢?

Android通過檢查我們當前的線程是否為UI線程從而拋出一個自定義的AndroidRuntimeException來提醒我們「Only the original thread that created a view hierarchy can touch its views」並強制終止程序運行,具體的實現在ViewRootImpl類的checkThread方法中:

@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
// 省去海量代碼…………………………

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new (
"Only the original thread that created a view hierarchy can touch its views.");
}
}

// 省去巨量代碼……………………
}
登錄後復制

這就是Android在4.0後對我們做出的一個限制。

其實造成這個現象的根本原因是:

還沒有到執行checkThread方法去檢查我們的當前線程那一步。」Android對UI事件的處理需要依賴於Message Queue,當一個Msg被壓入MQ到處理這個過程並非立即的,它需要一段事件,我們在線程中通過Thread.sleep(200)在等,在等什麼呢?在等ViewRootImpl的實例對象被創建。」

ViewRootImpl的實例對象是在OnResume中創建的啊!

看onResume方法的調度,其在ActivityThread中通過handleResumeActivity調度:

public final class ActivityThread {
// 省去海量代碼…………………………

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
unscheleGcIdler();

ActivityClientRecord r = performResumeActivity(token, clearHide);

if (r != null) {
final Activity a = r.activity;

// 省去無關代碼…………

final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}

} else if (!willBeVisible) {
// 省去無關代碼…………

r.hideForNow = true;
}

cleanUpPendingRemoveWindows(r);

if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
// 省去無關代碼…………

performConfigurationChanged(r.activity, r.newConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));
r.newConfig = null;
}

// 省去無關代碼…………

WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}

if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;

// 省去無關代碼…………

Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;

// 省去與ActivityManager的通信處理

} else {
// 省略異常發生時對Activity的處理邏輯
}
}

// 省去巨量代碼……………………
}
登錄後復制

handleResumeActivity方法邏輯相對要復雜一些,除了對當前顯示Window的邏輯判斷以及沒創建的初始化等等工作外其在最終會調用Activity的makeVisible方法

public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2 {
// 省去海量代碼…………………………

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}

// 省去巨量代碼……………………
}
登錄後復制

在makeVisible方法中邏輯相當簡單,獲取一個窗口管理器對象並將根視圖DecorView添加到其中,addView的具體實現在WindowManagerGlobal中:

public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 省去很多代碼

ViewRootImpl root;

// 省去一行代碼

synchronized (mLock) {
// 省去無關代碼

root = new ViewRootImpl(view.getContext(), display);

// 省去一行代碼

// 省去一行代碼

mRoots.add(root);

// 省去一行代碼
}

// 省去部分代碼
}
}
登錄後復制

在addView生成了一個ViewRootImpl對象並將其保存在了mRoots數組中,每當我們addView一次,就會生成一個ViewRootImpl對象,其實看到這里我們還可以擴展一下問題一個APP是否可以擁有多個根視圖呢?答案是肯定的,因為只要我調用了addView方法,我們傳入的View參數就可以被認為是一個根視圖,但是!在framework的默認實現中有且僅有一個根視圖,那就是我們上面makeVisible方法中addView進去的DecorView,所以為什麼我們可以說一個APP雖然可以有多個Activity,但是每個Activity只會有一個Window一個DecorView一個ViewRootImpl,看到這里很多童鞋依然會問,也就是說在onResume方法被執行後我們的ViewRootImpl才會被生成對吧,但是為什麼下面的代碼依然可以正確運行呢:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});

thread.start();
}

@Override
protected void onResume() {
super.onResume();
}
}
登錄後復制

Activity.onResume前,ViewRootImpl實例沒有建立,所以沒有checkThread檢查。但是使用了Thread.sleep(200)的時候,ViewRootImpl已經被創建完畢了,自然checkThread就起作用了,拋出異常順理成章。

第一種做法中,雖然是在子線程中setText,但是這時候View還沒畫出來呢,所以並不會調用之後的invalidate,而相當於只是設置TextView的一個屬性,不會invalidate,就沒有後面的那些方法調用了,歸根結底,就不會調用ViewRootImpl的checkThread,也就不會報錯。而第二種方法,調用setText之後,就會引發後面的一系列的方法調用,VIew要刷新界面,ViewGroup要更新布局,計運算元View的大小位置,到最後,ViewRootImpl就會checkThread,就崩了。

所以,嚴格上來說,第一種方法雖然在子線程了設置View屬性,但是不能夠歸結到」更新View」的范疇,因為還沒畫出來呢,就沒有所謂的更新。

當我們執行Thread.sleep時候,這時候onStart、onResume都執行了,子線程再調用setText的時候,就會崩潰。

那麼說,在onStart()或者onResume()裡面執行線程操作UI也是可以的:

@Override
protected void onStart() {
super.onStart();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});
thread.start();
}
登錄後復制

@Override
protected void onResume() {
super.onResume();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});
thread.start();
}
登錄後復制

注意的是:當你在來回切換界面的時候,onStart()和onResume()是會再執行一遍的,這時候程序就崩潰了!

1、能不能在非UI線程中更新UI呢?
答案:能、當然可以

2、View的運行和Activity的生命周期有什麼必然聯系嗎?
答案:沒有、或者隱晦地說沒有必然聯系

3、除了Handler外是否還有更簡便的方式在非UI線程更新UI呢?
答案:有、而且還不少,Activity.runOnUiThread(Runnable)、View.Post(Runnable)、View.PostDelayed(Runnable,long)、AsyncTask、其內部實現原理都是向此View的線程的內部消息隊列發送一個Message消息,並傳送數據和處理方式,省去了自己再寫一個專門的Handler去處理。

4、在子線程裡面用Toast也會報錯,加上Looper.prepare和Looper.loop就可以了,這里可以這樣做嗎?
答案當然是不可以。Toast和View本質上是不一樣的,Toast在子線程報錯,是因為Toast的顯示需要添加到一個MessageQueue中,然後Looper取出來,發給Handler調用顯示,子線程因為沒有Looper,所以需要加上Looper.prepare和Looper.loop創建一個Looper,但是實質上,這還是在子線程調用,所以還是會報錯的!

5、為什麼Android要求只能在UI主線程中更改View呢
這就要說到Android的單線程模型了,因為如果支持多線程修改View的話,由此產生的線程同步和線程安全問題將是非常繁瑣的,所以Android直接就定死了,View的操作必須在UI線程,從而簡化了系統設計。

參考文章
為什麼我們可以在非UI線程中更新UI
【Android開發經驗】來來來,同學,咱們討論一下「只能在UI主線程更新View」這件小事
線程
android
ui
女式涼鞋,時尚,優雅,透氣,貨到付款!
精選推薦
廣告

可能是全網最簡單透徹的安卓子線程更新 UI 解析
71閱讀·0評論·0點贊
2019年4月24日
android 不能在子線程中更新ui的討論和分析
1.5W閱讀·7評論·14點贊
2016年1月26日
android.view.ViewRootImpl$: Only the original thread that created
140閱讀·0評論·0點贊
2022年9月23日
Looper.prepare()和Looper.loop(),在子線程中更新UI
2520閱讀·0評論·0點贊
2016年5月2日
Android為什麼能在子線程中更新UI
305閱讀·0評論·1點贊
2020年4月9日
android多線程中更新ui,Android 在子線程中更新UI
146閱讀·0評論·0點贊
2021年6月3日
Android子線程更新UI就會Crash么
1781閱讀·0評論·4點贊
2017年4月1日
為什麼只能在主線程中操作UI?為什麼子線程中setText不報錯?
3970閱讀·1評論·5點贊
2017年6月27日
Android 子線程更新TextView的text 不拋出異常原因 分析總結
1211閱讀·0評論·3點贊
2019年7月10日
非主線程更新UI
210閱讀·0評論·0點贊
2018年4月12日
非 UI 線程中更新 UI
215閱讀·0評論·0點贊
2021年4月29日
【Android】 Handler——子線程更新UI
722閱讀·1評論·4點贊
2019年12月29日
android-如何在子線程中更新ui
4019閱讀·4評論·2點贊
2016年8月23日
SurfaceView
251閱讀·0評論·0點贊
2019年3月8日
非UI線程中更新UI
373閱讀·0評論·0點贊
2018年7月10日
QT非UI線程更新UI(跨線程更新UI)
275閱讀·0評論·0點贊
2022年9月21日
Android開發之UI線程和非UI線程
1619閱讀·0評論·1點贊
2020年4月5日
非UI線程可不可以更新UI(一)
1265閱讀·0評論·2點贊
2016年2月29日
為什麼我們可以在非UI線程中更新UI
2.8W閱讀·56評論·35點贊
2015年2月3日
去首頁
看看更多熱門內容

D. android怎麼更新UI

首先,android的UI刷新是在主線程(UI線程)中完成的。四大組件中,activity和service運行在主線程中。現在總結自己在項目中常用到的UI刷新方式。
第一,利用子線程發消息刷新UI。
子線程負責處理UI需要的數據,然後發消息到主線程來刷新UI。代碼結構如下:
new Thread(new Runnable() {

@Override
public void run() {
Person person=new Person();
person.setName(mName.getText().toString().trim());
person.setPhone(mPhone.getText().toString().trim());
Log.i("person",person.toString());
DatabaseInfoFactory.getPersonDao(mContext).addPerson(person);
Looper.prepare();
Message msg=Message.obtain();
msg.what=0x123456;
handler.sendMessage(msg);
Looper.loop();

}
}).start();
主線程中:
private Handler mHandler=new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what==0x123456||msg.what==0x123){
fillData();
setListener();
}

}
};
第二,利用非同步任務更新UI。代碼結構如下:
new AsyncTask<void,void,void>() {

@Override
protected void onPostExecute(Void result) {

if(mAdapter==null){
mAdapter=new LeaveInfoAdapter();
//設置數據適配器
mLVleaveInfos.setAdapter(mAdapter);
Log.i("測試", "非同步任務顯示後台獲得資料庫數據");
}
else {
mAdapter.notifyDataSetChanged();

}

super.onPostExecute(result);
}

@Override
protected Void doInBackground(Void... params) {
//獲得要顯示的數據
mleaveInfos=mLeaveInfosDao.findAll();
if (mleaveInfos==null) {
Toast.makeText(HomeActivity.this,"請假數據不存在或是已經清除!", 500).show();

}

Log.i("測試", "非同步任務後台獲得資料庫數據"+mleaveInfos.size());

return null;
}
}.execute();</void,void,void>
第三,利用配置文件+activity的生命周期方法刷新UI。

E. android中如何實現UI的實時更新

1、在主線程中啟動一個子線程

首先,我們需要在主線程中啟動一個子線程,這個比較簡單,直接在MainActivity的onCreate()方法中調用如下方法即可:

newThread(mRunnable).start();

2、在子線程中發送Message給Handler

在創建子線程時,我們使用了Runnable介面對象mRunnable。這里,只需要實現Runnable介面,並重寫該介面的run()方法,在run()方法中實現每1秒發送一條Message給Handler即可。具體實現方法如下:

/*
*Function:實現run()方法,每1秒發送一條Message給Handler
*/
privateRunnablemRunnable=newRunnable(){
publicvoidrun(){
while(true){
try{
Thread.sleep(1000);
mHandler.sendMessage(mHandler.obtainMessage());
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
};

3、Handler接收Message通知

最後,我們創建一個Handler對象,用來接收Message通知。在收到Message通知後,完成刷新UI的操作即可。具體實現方法如下:

/*
*Function:實現handleMessage()方法,用於接收Message,刷新UI
*/
privateHandlermHandler=newHandler(){
publicvoidhandleMessage(Messagemsg){
super.handleMessage(msg);
refreshUI();
}
};

4、刷新UI

由以上的代碼可以看出,刷新UI的操作,我們是放在refreshUI()方法中來完成的。refreshUI()方法的實現也很簡單,調用HttpUtils工具類中的getInputStream()方法,獲得圖1所示Web工程的頁面內容輸入流,再將該輸入流轉化為字元串,放入TextView控制項中進行顯示即可。具體實現方法如下:

/*
*Function:刷新UI
*/
privatevoidrefreshUI(){
try{
InputStreaminputStream=HttpUtils.getInputStream();
StringresultData=HttpUtils.getResultData(inputStream);
mTextView.setText(resultData);
}catch(IOExceptione){
e.printStackTrace();
}
}

F. android studio子線程更新UI 問題

  1. 睡使用Thread.currentthread().sleep(1000);

  2. 發送不同的Message,每次都是一個,放到for循環內Message message = new Message();

G. Android 在子線程中更新UI的幾種方法示例

請您慢慢看:

直接在UI線程中開啟子線程來更新TextView顯示的內容,運行程序我們會發現,如下錯誤:android.view.ViewRoot$: Only the original thread that created a view hierarchy can touch its views.翻譯過來就是:只有創建這個控制項的線程才能去更新該控制項的內容。

所有的UI線程要去負責View的創建並且維護它,例如更新冒個TextView的顯示,都必須在主線程中去做,我們不能直接在UI線程中去創建子線程,要利用消息機制:handler,如下就是handler的簡單工作原理圖:

既然android給我們提供了Handler機制來解決這樣的問題,請看如下代碼:

public class HandlerTestActivity extends Activity { private TextView tv; private static final int UPDATE = 0; private Handler handler = new Handler() { @Overridepublic void handleMessage(Message msg) { // TODO 接收消息並且去更新UI線程上的控制項內容if (msg.what == UPDATE) { // Bundle b = msg.getData();// tv.setText(b.getString("num")); tv.setText(String.valueOf(msg.obj)); } super.handleMessage(msg); } }; /** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) findViewById(R.id.tv); new Thread() { @Overridepublic void run() { // TODO 子線程中通過handler發送消息給handler接收,由handler去更新TextView的值try { for (int i = 0; i < 100; i++) { Thread.sleep(500); Message msg = new Message(); msg.what = UPDATE; // Bundle b = new Bundle();// b.putString("num", "更新後的值:" + i);// msg.setData(b); msg.obj = "更新後的值:" + i; handler.sendMessage(msg); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}

我們就通過Handler機制來處理了子線程去更新UI線程式控制制項問題,Andrid開發中要經常用到這種機制。

閱讀全文

與android子線程中更新ui相關的資料

熱點內容
51單片機課程設計課題 瀏覽:891
手機淘寶登錄怎麼加密碼 瀏覽:481
linux快捷方式圖標 瀏覽:33
陽光車險的app叫什麼名字 瀏覽:461
購買單片機的器件時需要給商家啥 瀏覽:534
並行編譯技術的發展 瀏覽:549
阿里雲伺服器安裝管理 瀏覽:550
java手機開發教程 瀏覽:674
我的世界怎麼刪除伺服器數據 瀏覽:671
linux內存子系統 瀏覽:972
加密思維幣 瀏覽:690
魅族訪客文件夾 瀏覽:52
添加的文件夾怎麼找 瀏覽:617
程序員涉黃 瀏覽:700
maven編譯resources下的js 瀏覽:521
ubuntu文件移動命令 瀏覽:229
安卓i怎麼查找蘋果手機 瀏覽:951
雲伺服器宕機概率 瀏覽:232
在線買葯用什麼app知乎 瀏覽:815
ubuntu解壓xz文件 瀏覽:676