当前位置: 首页 > news >正文

最打动人心的广告语下载优化大师

最打动人心的广告语,下载优化大师,大连网站优化公司,从零开始学android编程一、ThreadLocal 1、什么是ThreadLocal ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。 一般来说&#xf…

一、ThreadLocal

1、什么是ThreadLocal

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。

2、ThreadLocal 使用

1)、创建ThreadLocal

ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();

通过泛型指定ThreadLocal中存储的数据类型
2)、ThreadLocal保存参数

threadLocal.set(true);

threadLocal中保存参数true
3)、获取参数

threadLocal.get()

下面是一个使用ThreadLocal的例子:

final ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
threadLocal.set(true);
Log.d(TAG, "main thread:" + threadLocal.get());new Thread() {@Overridepublic void run() {super.run();// 这里打印的值不是false是null,因为ThreadLocal保存的值在不同线程是两个不同的对象Log.d(TAG, "child thread:" + threadLocal.get());}
}.start();

打印出日志结果如下:
D/ThreadLoalActivity: main thread:true
D/ThreadLoalActivity: child thread:null
在主线程中把ThreadLocal设置为true,但是在子线程获取的值是null。因为ThreadLocal的作用域是线程,在不同线程中相当于两个对象,所以子线程中没有赋值就是null。

3、ThreadLocal的工作原理

1)set方法

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}

首先获取当前线程,通过getMap获取当前线程中保存的ThreadLocalMap ,getMap源码如下:

ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}

实际上是获取Thread中的threadLocals对象,即每个线程中有一个ThreadLocalMap (我们可以简单理解为HashMap),ThreadLocalMap 用于保存数据。因为可能会定义多个ThreadLocal(注意在Android中Looper中的ThreadLocal是一个静态变量,因此Looper中只有唯一一个ThreadLocal),所以用Map保存。 Thread 中的threadLocals定义如下:

class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;}

ThreadLocalMap是ThreadLocal的内部类,ThreadLocalMap源码如下:

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}private Entry[] table;
}

在 ThreadLocalMap 内部有一个数组,private Entry[] table,ThrealLocal 的值就存在这个 table 数组中。Entry的key保存ThreadLocal对象,value保存ThrealLocal 的值。
2)、get方法

public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();
}

取出当前线程的 ThreadLocalMap 对象,然后取出它的 table 数组,然后获取保存在ThreadLocal中的值。

二、消息队列MessageQueue的工作原理

MessageQueue主要包含两个操作:插入和读取。读取本身伴随着删除操作,对应着两个方法分别是enqueueMessage()和next()。enqueueMessage是插入一条消息到消息队列中,next是取出一条信息并将其从队列移除。看他的方法源码可以看到enqueueMessage主要是进行单链表的插入操作。next是一个无线循环,如果么有消息,next会一直阻塞在这里,如果有消息到来,next会返回这个消息并将其从队列移除。
enqueueMessage源码如下:

boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;
}

next源码如下:

Message next() {...int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// Stalled by a barrier.  Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// Next message is not ready.  Set a timeout to wake up when it is ready.nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// Got a message.mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;if (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();return msg;}} else {// No more messages.nextPollTimeoutMillis = -1;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}}...
}

第27行nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);计算距离下一个消息执行的时间。第10行nativePollOnce(ptr, nextPollTimeoutMillis);是本地方法,C语言实现。会阻塞当前线程,直到时间达到nextPollTimeoutMillis 。
上面使用synchronized 对插入和取出操作进行加锁,因为Handler sendMsg时可能在不同的线程中。

三、Looper的工作原理

Looper会不停的从MessageQueue中查看是否有新消息,有的话立刻处理,没有就会一直阻塞。Looper在Android消息机制中扮演消息循环的角色。

1、怎么创建一个Looper

1)、Looper.prepare()
Looper.prepare()源码如下:

public static void prepare() {prepare(true);
}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));
}

在prepare中新建了一个Looper对象,并且将这个对象保存在ThreadLocal中。
2)、Looper.prepareMainLooper()
这里是创建主线程的Looper,注意这个方法是系统调用的,不需要我们调用,系统默认就初始化了主线程的Looper(详见第下面五节)。所以我们在主线程可以直接new Handler()使用,如果在子线程还必须调用Looper.prepare()和Looper.loop()(详见下面第六节)
Looper.prepareMainLooper()源码如下;

public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}
}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));
}

先调用prepare(),在prepare中新建了一个Looper对象,并且将这个对象保存在ThreadLocal中。注意这里prepare传入的参数是false,表示消息队列不允许退出。然后将Looper保存在变量sMainLooper中。

2、开启循环

新建Looper后并不能正常工作,还需要调用Looper.loop()启动消息循环。Loop源码如下:

public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();final long end;try {msg.target.dispatchMessage(msg);end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (slowDispatchThresholdMs > 0) {final long time = end - start;if (time > slowDispatchThresholdMs) {Slog.w(TAG, "Dispatch took " + time + "ms on "+ Thread.currentThread().getName() + ", h=" +msg.target + " cb=" + msg.callback + " msg=" + msg.what);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}
}

调用Message msg = queue.next()检测消息队列中是否有消息,有消息就继续向下执行,没有就阻塞。有消息时就会执行到msg.target.dispatchMessage(msg),target就是Handler。相当于调用的是Handler.dispatchMessage(msg)。Handler的sendMessage方法最终会调用enqueueMessage方法,enqueueMessage方法源码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

msg.target = this;就是把当前Handler的实例保存到 msg.target中。

什么时候会退出这个loop死循环呢?
Looper.loop()是一个死循环,唯一跳出的方式就是MessageQueue的next方法返回null。当Looper的quit方法被调用时,Looper会调用MessageQueue的quit或者quitSafely方法通知消息队列退出,当消息队列被标记为退出状态时,它的next方法返回null。也就是说Looper必须退出,否则loop方法就会无限循环下去。

四、Handler的工作原理

Handler的内部实现其实就是Looper和MessageQueue。创建Handler的构造方法最终都会执行如下方法,源码如下:

public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;
}

mLooper = Looper.myLooper();获取了当前线程的Looper, mQueue = mLooper.mQueue;获取了Looper对应的消息队列。注意主线程系统自动初始化Looper,子线程需要自己初始化Looper。
Handler发送消息的方法最终都会执行如下方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);
}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

实际就是向消息队列中添加消息,然后在Looper中会去处理。
Looper怎么处理,如何回调到Handler中的handleMessage见“3.2 开启循环”的分析。

五、主线程的消息循环

当我们启动一个程序(即点击Launch中的按钮)时,zygote会fork 一个进程,会给每个应用分配独立的JVM。Java程序都是从main函数开始启动,所以Android应用最先执行的也是main函数。而这个main函数在ActivityTread中。
Android的主线程就是ActivityTread,入口方法是main,在main方法中会通过Looper.prepareMainLooper()来创建主线程的Looper和MessageQueue,然后通过Looper.loop()开启消息循环,源码如下:

public static void main(String[] args) {...Looper.prepareMainLooper();ActivityThread thread = new ActivityThread(); //在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}...Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

主线程消息循环开始以后还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H,H定义的消息类型如下:

private class H extends Handler {public static final int LAUNCH_ACTIVITY         = 100;public static final int PAUSE_ACTIVITY          = 101;public static final int PAUSE_ACTIVITY_FINISHING= 102;public static final int STOP_ACTIVITY_SHOW      = 103;public static final int STOP_ACTIVITY_HIDE      = 104;public static final int SHOW_WINDOW             = 105;public static final int HIDE_WINDOW             = 106;public static final int RESUME_ACTIVITY         = 107;public static final int SEND_RESULT             = 108;public static final int DESTROY_ACTIVITY        = 109;public static final int BIND_APPLICATION        = 110;public static final int EXIT_APPLICATION        = 111;public static final int NEW_INTENT              = 112;public static final int RECEIVER                = 113;public static final int CREATE_SERVICE          = 114;public static final int SERVICE_ARGS            = 115;public static final int STOP_SERVICE            = 116;public static final int CONFIGURATION_CHANGED   = 118;public static final int CLEAN_UP_CONTEXT        = 119;public static final int GC_WHEN_IDLE            = 120;public static final int BIND_SERVICE            = 121;public static final int UNBIND_SERVICE          = 122;public static final int DUMP_SERVICE            = 123;public static final int LOW_MEMORY              = 124;public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;public static final int RELAUNCH_ACTIVITY       = 126;public static final int PROFILER_CONTROL        = 127;public static final int CREATE_BACKUP_AGENT     = 128;public static final int DESTROY_BACKUP_AGENT    = 129;public static final int SUICIDE                 = 130;public static final int REMOVE_PROVIDER         = 131;public static final int ENABLE_JIT              = 132;public static final int DISPATCH_PACKAGE_BROADCAST = 133;public static final int SCHEDULE_CRASH          = 134;public static final int DUMP_HEAP               = 135;public static final int DUMP_ACTIVITY           = 136;public static final int SLEEPING                = 137;public static final int SET_CORE_SETTINGS       = 138;public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;public static final int TRIM_MEMORY             = 140;public static final int DUMP_PROVIDER           = 141;public static final int UNSTABLE_PROVIDER_DIED  = 142;public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;public static final int INSTALL_PROVIDER        = 145;public static final int ON_NEW_ACTIVITY_OPTIONS = 146;public static final int CANCEL_VISIBLE_BEHIND = 147;public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;public static final int ENTER_ANIMATION_COMPLETE = 149;
}

H中处理消息的handMeassge

public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {case LAUNCH_ACTIVITY: {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");final ActivityClientRecord r = (ActivityClientRecord) msg.obj;r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);handleLaunchActivity(r, null);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;case RELAUNCH_ACTIVITY: {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");ActivityClientRecord r = (ActivityClientRecord)msg.obj;handleRelaunchActivity(r);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;case PAUSE_ACTIVITY:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,(msg.arg1&2) != 0);maybeSnapshot();Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case PAUSE_ACTIVITY_FINISHING:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,(msg.arg1&1) != 0);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case STOP_ACTIVITY_SHOW:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");handleStopActivity((IBinder)msg.obj, true, msg.arg2);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case STOP_ACTIVITY_HIDE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");handleStopActivity((IBinder)msg.obj, false, msg.arg2);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;...}

可看到有启动Activity,暂停Activity,停止Activity。

ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。

六、子线程如何创建消息循环

主线程消息循环的创建是系统自动完成的,子线程的消息循环的创建需要我们自己手动完成 。子线程创建消息循环需要3步,具体步骤见下面代码注释。
代码如下:

new Thread() {@Overridepublic void run() {// 1、为当前线程新建LooperLooper.prepare();// 2、新建Handler// handler = new Handler(new Handler.Callback() { 这个等价于下面这行代码handler = new Handler(Looper.myLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_ONE:Log.d(TAG, "handleMessage>>MSG_ONE");break;case MSG_TWO:Log.d(TAG, "handleMessage>>MSG_TWO");break;}return false;}});// 3、开启循环Looper.loop();// 4、使用完后调用quitSafely结束Looper的死循环,这样子线程才能结束,子线程才能回收Looper.myLooper().quitSafely();}
}.start();

注意上面Handler的工作线程是子线程。

总结

1、ThreadLocal会根据当前线程取出该线程对应的ThreadLocalMap(类似HashMap),ThreadLocal.set调用ThreadLocalMap.set(当前的ThreadLocal, value),即ThreadLocalMap使用ThreadLocal作为key。ThreadLocalMap中是把key、value封装成Entry放在一个数组中。
2、Android的消息机制主要就是Handler、Looper、MessageQueue三个类。
3、MessageQueue使用next读取数据。next是一个无线循环,如果没有消息,next会一直阻塞在这里,如果有消息到来,next会返回这个消息并将其从队列移除。
4、我们在new Handler()前必须先调用Looper.prepare(),否则会因为当前线程没有Looper报异常。线程最后还要调用Looper.loop()启动循环。在主线程中我们不用手动调用Looper.prepare()和loop(),因为应用启动时系统帮我们调用了Looper.prepare()和loop()。
5、Handler中包含Looper和MessageQueue,Handler发一个消息实际就是向MessageQueue中添加一个消息,Looper.loop()中循环调用MessageQueue.next()检测是否有新消息,有就处理,没有就一直检测。
6、Activity的生命周期都是在主线程的Looper.loop中收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。
7、创建Message不使用new,使用Message.obtain()。Message中会使用单链表维护一个池,保存new出的Message对象。这样的好处时避免频繁创建对象,造成内存抖动。因为Message会系统很多地方都会使用,触摸屏幕然后响应都会使用Message,即系统每时每刻都在新建Message的对象。如果使用new,用完会释放,这样会造成很多内存碎片。其他对象分配时可能不够分配空间就会造成OOM。这中使用单链表复用就是享元模式。

http://www.zhongyajixie.com/news/58459.html

相关文章:

  • 电子商务网站建设基础项目实训报告防止恶意点击软件管用吗
  • php做用户注册网站网址域名大全
  • 福州市城乡建设局网站网络推广人员是干什么的
  • 专业的咨询行业网站制作外贸建站公司
  • 石家庄网站建设人员seo是什么服
  • 做购物网站的数据库域名权重查询工具
  • 灰色网站如何推广如何做推广和引流
  • 做服饰网站seo技术蜘蛛屯
  • 营销手机网站制作广告公司名称
  • 常见的o2o平台有哪些seo实战培训中心
  • 二级域名网站好不好人工智能培训心得
  • 长春网站建设公司青岛网站
  • 钢琴网站建设原则网站软文是什么
  • 爱有声小说网站捡个校花做老婆域名注册查询网站
  • 0基础怎么做网站模版百度指数是什么意思
  • 网站备案好不好西安seo外包行者seo
  • 阜新公司做网站如何制作网址
  • 网页界面设计的意义网站seo优化的目的
  • 重庆网站建设哪家公司好电脑全自动挂机赚钱
  • 丹东做网站的公司培训学校怎么招生
  • 手机版网站如何制作西安做网站哪家好
  • 牛商网做网站谷歌浏览器下载视频
  • 栾川网站开发外贸网站seo优化
  • 找别人做网站一般注意什么淘宝seo优化是什么意思
  • 网站开发做网站合肥seo培训
  • 网站建设市场报价app排名优化
  • 平潭建设局网站首页软文范例大全500字
  • 用python做网站优点app拉新推广
  • 做视频网站需要执照吗百度移动端关键词优化
  • 使用php做的网站有哪些百度域名注册官网