【Android测试】【第三节】ADB——源码浅谈

前言



  由于本人水平有限,所以这里简单的说说ADB源码。

  首先根据前面的理解,我们已经知道了ADB是“连接手机和PC的一个桥梁”,我们经常在PC端开发的时候,会用到eclipse这个工具,这里面有一个工具叫DDMS,如下图:

 

  是不是发现通过DDMS在PC端可以看到手机的一些信息,其实呢 它就是通过 “ddmlib.jar” 来建立起ADB的。因此我们今天就通过反编译 “ddmlib.jar” 来分析一下ADB源码。

反编译



  首先不得不吐槽一下百度经验的审核人员,我看到里面“反编译jar”的经验没有,于是呢我就写了一个提交了上去,结果提交了很多次,都给我打回了,真不知道这帮审核的人员是怎么想的,这种方便别人参考的内容难道不应该被通过吗?切

  好了,说正事吧。

  ddmlib.jar 放在 <SDk path>\tools\libs 的文件夹下。

  整个反编译的过程如下:

    1、下载jd-gui-0.3.3.windows.zip (我的微云链接:http://url.cn/Zz8sOj )

    2、解压之后打开,将要编译的jar导入:

    3、展开坐标的树形结构,就是源码啦

上源码



   1 /*
   2  * Copyright (C) 2007 The Android Open Source Project
   3  *
   4  * Licensed under the Apache License, Version 2.0 (the "License");
   5  * you may not use this file except in compliance with the License.
   6  * You may obtain a copy of the License at
   7  *
   8  *      http://www.apache.org/licenses/LICENSE-2.0
   9  *
  10  * Unless required by applicable law or agreed to in writing, software
  11  * distributed under the License is distributed on an "AS IS" BASIS,
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  * See the License for the specific language governing permissions and
  14  * limitations under the License.
  15  */
  16
  17 package org.athrun.ddmlib;
  18
  19
  20 import java.io.BufferedReader;
  21 import java.io.IOException;
  22 import java.io.InputStreamReader;
  23 import java.lang.Thread.State;
  24 import java.net.InetAddress;
  25 import java.net.InetSocketAddress;
  26 import java.net.UnknownHostException;
  27 import java.security.InvalidParameterException;
  28 import java.util.ArrayList;
  29 import java.util.Map;
  30 import java.util.regex.Matcher;
  31 import java.util.regex.Pattern;
  32
  33 import org.athrun.ddmlib.Log.LogLevel;
  34
  35 /**
  36  * A connection to the host-side android debug bridge (adb)
  37  * <p/>
  38  * This is the central point to communicate with any devices, emulators, or the
  39  * applications running on them.
  40  * <p/>
  41  * <b>{@link #init(boolean)} must be called before anything is done.</b>
  42  */
  43 public final class AndroidDebugBridge {
  44
  45     /*
  46      * Minimum and maximum version of adb supported. This correspond to
  47      * ADB_SERVER_VERSION found in //device/tools/adb/adb.h
  48      */
  49
  50     private final static int ADB_VERSION_MICRO_MIN = 20;
  51     private final static int ADB_VERSION_MICRO_MAX = -1;
  52
  53     private final static Pattern sAdbVersion = Pattern
  54             .compile("^.*(\\d+)\\.(\\d+)\\.(\\d+)$"); //$NON-NLS-1$
  55
  56     private final static String ADB = "adb"; //$NON-NLS-1$
  57     private final static String DDMS = "ddms"; //$NON-NLS-1$
  58     private final static String SERVER_PORT_ENV_VAR = "ANDROID_ADB_SERVER_PORT"; //$NON-NLS-1$
  59
  60     // Where to find the ADB bridge.
  61     final static String ADB_HOST = "127.0.0.1"; //$NON-NLS-1$
  62     final static int ADB_PORT = 5037;
  63
  64     private static InetAddress sHostAddr;
  65     private static InetSocketAddress sSocketAddr;
  66
  67     private static AndroidDebugBridge sThis;
  68     private static boolean sInitialized = false;
  69     private static boolean sClientSupport;
  70
  71     /** Full path to adb. */
  72     private String mAdbOsLocation = null;
  73
  74     private boolean mVersionCheck;
  75
  76     private boolean mStarted = false;
  77
  78     private DeviceMonitor mDeviceMonitor;
  79
  80     private final static ArrayList<IDebugBridgeChangeListener> sBridgeListeners = new ArrayList<IDebugBridgeChangeListener>();
  81     private final static ArrayList<IDeviceChangeListener> sDeviceListeners = new ArrayList<IDeviceChangeListener>();
  82     private final static ArrayList<IClientChangeListener> sClientListeners = new ArrayList<IClientChangeListener>();
  83
  84     // lock object for synchronization
  85     private static final Object sLock = sBridgeListeners;
  86
  87     /**
  88      * Classes which implement this interface provide a method that deals with
  89      * {@link AndroidDebugBridge} changes.
  90      */
  91     public interface IDebugBridgeChangeListener {
  92         /**
  93          * Sent when a new {@link AndroidDebugBridge} is connected.
  94          * <p/>
  95          * This is sent from a non UI thread.
  96          *
  97          * @param bridge
  98          *            the new {@link AndroidDebugBridge} object.
  99          */
 100         public void bridgeChanged(AndroidDebugBridge bridge);
 101     }
 102
 103     /**
 104      * Classes which implement this interface provide methods that deal with
 105      * {@link IDevice} addition, deletion, and changes.
 106      */
 107     public interface IDeviceChangeListener {
 108         /**
 109          * Sent when the a device is connected to the {@link AndroidDebugBridge}
 110          * .
 111          * <p/>
 112          * This is sent from a non UI thread.
 113          *
 114          * @param device
 115          *            the new device.
 116          */
 117         public void deviceConnected(IDevice device);
 118
 119         /**
 120          * Sent when the a device is connected to the {@link AndroidDebugBridge}
 121          * .
 122          * <p/>
 123          * This is sent from a non UI thread.
 124          *
 125          * @param device
 126          *            the new device.
 127          */
 128         public void deviceDisconnected(IDevice device);
 129
 130         /**
 131          * Sent when a device data changed, or when clients are
 132          * started/terminated on the device.
 133          * <p/>
 134          * This is sent from a non UI thread.
 135          *
 136          * @param device
 137          *            the device that was updated.
 138          * @param changeMask
 139          *            the mask describing what changed. It can contain any of
 140          *            the following values: {@link IDevice#CHANGE_BUILD_INFO},
 141          *            {@link IDevice#CHANGE_STATE},
 142          *            {@link IDevice#CHANGE_CLIENT_LIST}
 143          */
 144         public void deviceChanged(IDevice device, int changeMask);
 145     }
 146
 147     /**
 148      * Classes which implement this interface provide methods that deal with
 149      * {@link Client} changes.
 150      */
 151     public interface IClientChangeListener {
 152         /**
 153          * Sent when an existing client information changed.
 154          * <p/>
 155          * This is sent from a non UI thread.
 156          *
 157          * @param client
 158          *            the updated client.
 159          * @param changeMask
 160          *            the bit mask describing the changed properties. It can
 161          *            contain any of the following values:
 162          *            {@link Client#CHANGE_INFO},
 163          *            {@link Client#CHANGE_DEBUGGER_STATUS},
 164          *            {@link Client#CHANGE_THREAD_MODE},
 165          *            {@link Client#CHANGE_THREAD_DATA},
 166          *            {@link Client#CHANGE_HEAP_MODE},
 167          *            {@link Client#CHANGE_HEAP_DATA},
 168          *            {@link Client#CHANGE_NATIVE_HEAP_DATA}
 169          */
 170         public void clientChanged(Client client, int changeMask);
 171     }
 172
 173     /**
 174      * Initializes the <code>ddm</code> library.
 175      * <p/>
 176      * This must be called once <b>before</b> any call to
 177      * {@link #createBridge(String, boolean)}.
 178      * <p>
 179      * The library can be initialized in 2 ways:
 180      * <ul>
 181      * <li>Mode 1: <var>clientSupport</var> == <code>true</code>.<br>
 182      * The library monitors the devices and the applications running on them. It
 183      * will connect to each application, as a debugger of sort, to be able to
 184      * interact with them through JDWP packets.</li>
 185      * <li>Mode 2: <var>clientSupport</var> == <code>false</code>.<br>
 186      * The library only monitors devices. The applications are left untouched,
 187      * letting other tools built on <code>ddmlib</code> to connect a debugger to
 188      * them.</li>
 189      * </ul>
 190      * <p/>
 191      * <b>Only one tool can run in mode 1 at the same time.</b>
 192      * <p/>
 193      * Note that mode 1 does not prevent debugging of applications running on
 194      * devices. Mode 1 lets debuggers connect to <code>ddmlib</code> which acts
 195      * as a proxy between the debuggers and the applications to debug. See
 196      * {@link Client#getDebuggerListenPort()}.
 197      * <p/>
 198      * The preferences of <code>ddmlib</code> should also be initialized with
 199      * whatever default values were changed from the default values.
 200      * <p/>
 201      * When the application quits, {@link #terminate()} should be called.
 202      *
 203      * @param clientSupport
 204      *            Indicates whether the library should enable the monitoring and
 205      *            interaction with applications running on the devices.
 206      * @see AndroidDebugBridge#createBridge(String, boolean)
 207      * @see DdmPreferences
 208      */
 209     public static synchronized void init(boolean clientSupport) {
 210         if (sInitialized) {
 211             throw new IllegalStateException(
 212                     "AndroidDebugBridge.init() has already been called.");
 213         }
 214         sInitialized = true;
 215         sClientSupport = clientSupport;
 216
 217         // Determine port and instantiate socket address.
 218         initAdbSocketAddr();
 219
 220         MonitorThread monitorThread = MonitorThread.createInstance();
 221         monitorThread.start();
 222
 223         HandleHello.register(monitorThread);
 224         HandleAppName.register(monitorThread);
 225         HandleTest.register(monitorThread);
 226         HandleThread.register(monitorThread);
 227         HandleHeap.register(monitorThread);
 228         HandleWait.register(monitorThread);
 229         HandleProfiling.register(monitorThread);
 230         HandleNativeHeap.register(monitorThread);
 231     }
 232
 233     /**
 234      * Terminates the ddm library. This must be called upon application
 235      * termination.
 236      */
 237     public static synchronized void terminate() {
 238         // kill the monitoring services
 239         if (sThis != null && sThis.mDeviceMonitor != null) {
 240             sThis.mDeviceMonitor.stop();
 241             sThis.mDeviceMonitor = null;
 242         }
 243
 244         MonitorThread monitorThread = MonitorThread.getInstance();
 245         if (monitorThread != null) {
 246             monitorThread.quit();
 247         }
 248
 249         sInitialized = false;
 250     }
 251
 252     /**
 253      * Returns whether the ddmlib is setup to support monitoring and interacting
 254      * with {@link Client}s running on the {@link IDevice}s.
 255      */
 256     static boolean getClientSupport() {
 257         return sClientSupport;
 258     }
 259
 260     /**
 261      * Returns the socket address of the ADB server on the host.
 262      */
 263     public static InetSocketAddress getSocketAddress() {
 264         return sSocketAddr;
 265     }
 266
 267     /**
 268      * Creates a {@link AndroidDebugBridge} that is not linked to any particular
 269      * executable.
 270      * <p/>
 271      * This bridge will expect adb to be running. It will not be able to
 272      * start/stop/restart adb.
 273      * <p/>
 274      * If a bridge has already been started, it is directly returned with no
 275      * changes (similar to calling {@link #getBridge()}).
 276      *
 277      * @return a connected bridge.
 278      */
 279     public static AndroidDebugBridge createBridge() {
 280         synchronized (sLock) {
 281             if (sThis != null) {
 282                 return sThis;
 283             }
 284
 285             try {
 286                 sThis = new AndroidDebugBridge();
 287                 sThis.start();
 288             } catch (InvalidParameterException e) {
 289                 sThis = null;
 290             }
 291
 292             // because the listeners could remove themselves from the list while
 293             // processing
 294             // their event callback, we make a copy of the list and iterate on
 295             // it instead of
 296             // the main list.
 297             // This mostly happens when the application quits.
 298             IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners
 299                     .toArray(new IDebugBridgeChangeListener[sBridgeListeners
 300                             .size()]);
 301
 302             // notify the listeners of the change
 303             for (IDebugBridgeChangeListener listener : listenersCopy) {
 304                 // we attempt to catch any exception so that a bad listener
 305                 // doesn‘t kill our
 306                 // thread
 307                 try {
 308                     listener.bridgeChanged(sThis);
 309                 } catch (Exception e) {
 310                     Log.e(DDMS, e);
 311                 }
 312             }
 313
 314             return sThis;
 315         }
 316     }
 317
 318     /**
 319      * Creates a new debug bridge from the location of the command line tool.
 320      * <p/>
 321      * Any existing server will be disconnected, unless the location is the same
 322      * and <code>forceNewBridge</code> is set to false.
 323      *
 324      * @param osLocation
 325      *            the location of the command line tool ‘adb‘
 326      * @param forceNewBridge
 327      *            force creation of a new bridge even if one with the same
 328      *            location already exists.
 329      * @return a connected bridge.
 330      */
 331     public static AndroidDebugBridge createBridge(String osLocation,
 332             boolean forceNewBridge) {
 333         synchronized (sLock) {
 334             if (sThis != null) {
 335                 if (sThis.mAdbOsLocation != null
 336                         && sThis.mAdbOsLocation.equals(osLocation)
 337                         && forceNewBridge == false) {
 338                     return sThis;
 339                 } else {
 340                     // stop the current server
 341                     sThis.stop();
 342                 }
 343             }
 344
 345             try {
 346                 sThis = new AndroidDebugBridge(osLocation);
 347                 sThis.start();
 348             } catch (InvalidParameterException e) {
 349                 sThis = null;
 350             }
 351
 352             // because the listeners could remove themselves from the list while
 353             // processing
 354             // their event callback, we make a copy of the list and iterate on
 355             // it instead of
 356             // the main list.
 357             // This mostly happens when the application quits.
 358             IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners
 359                     .toArray(new IDebugBridgeChangeListener[sBridgeListeners
 360                             .size()]);
 361
 362             // notify the listeners of the change
 363             for (IDebugBridgeChangeListener listener : listenersCopy) {
 364                 // we attempt to catch any exception so that a bad listener
 365                 // doesn‘t kill our
 366                 // thread
 367                 try {
 368                     listener.bridgeChanged(sThis);
 369                 } catch (Exception e) {
 370                     Log.e(DDMS, e);
 371                 }
 372             }
 373
 374             return sThis;
 375         }
 376     }
 377
 378     /**
 379      * Returns the current debug bridge. Can be <code>null</code> if none were
 380      * created.
 381      */
 382     public static AndroidDebugBridge getBridge() {
 383         return sThis;
 384     }
 385
 386     /**
 387      * Disconnects the current debug bridge, and destroy the object.
 388      * <p/>
 389      * This also stops the current adb host server.
 390      * <p/>
 391      * A new object will have to be created with
 392      * {@link #createBridge(String, boolean)}.
 393      */
 394     public static void disconnectBridge() {
 395         synchronized (sLock) {
 396             if (sThis != null) {
 397                 sThis.stop();
 398                 sThis = null;
 399
 400                 // because the listeners could remove themselves from the list
 401                 // while processing
 402                 // their event callback, we make a copy of the list and iterate
 403                 // on it instead of
 404                 // the main list.
 405                 // This mostly happens when the application quits.
 406                 IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners
 407                         .toArray(new IDebugBridgeChangeListener[sBridgeListeners
 408                                 .size()]);
 409
 410                 // notify the listeners.
 411                 for (IDebugBridgeChangeListener listener : listenersCopy) {
 412                     // we attempt to catch any exception so that a bad listener
 413                     // doesn‘t kill our
 414                     // thread
 415                     try {
 416                         listener.bridgeChanged(sThis);
 417                     } catch (Exception e) {
 418                         Log.e(DDMS, e);
 419                     }
 420                 }
 421             }
 422         }
 423     }
 424
 425     /**
 426      * Adds the listener to the collection of listeners who will be notified
 427      * when a new {@link AndroidDebugBridge} is connected, by sending it one of
 428      * the messages defined in the {@link IDebugBridgeChangeListener} interface.
 429      *
 430      * @param listener
 431      *            The listener which should be notified.
 432      */
 433     public static void addDebugBridgeChangeListener(
 434             IDebugBridgeChangeListener listener) {
 435         synchronized (sLock) {
 436             if (sBridgeListeners.contains(listener) == false) {
 437                 sBridgeListeners.add(listener);
 438                 if (sThis != null) {
 439                     // we attempt to catch any exception so that a bad listener
 440                     // doesn‘t kill our
 441                     // thread
 442                     try {
 443                         listener.bridgeChanged(sThis);
 444                     } catch (Exception e) {
 445                         Log.e(DDMS, e);
 446                     }
 447                 }
 448             }
 449         }
 450     }
 451
 452     /**
 453      * Removes the listener from the collection of listeners who will be
 454      * notified when a new {@link AndroidDebugBridge} is started.
 455      *
 456      * @param listener
 457      *            The listener which should no longer be notified.
 458      */
 459     public static void removeDebugBridgeChangeListener(
 460             IDebugBridgeChangeListener listener) {
 461         synchronized (sLock) {
 462             sBridgeListeners.remove(listener);
 463         }
 464     }
 465
 466     /**
 467      * Adds the listener to the collection of listeners who will be notified
 468      * when a {@link IDevice} is connected, disconnected, or when its properties
 469      * or its {@link Client} list changed, by sending it one of the messages
 470      * defined in the {@link IDeviceChangeListener} interface.
 471      *
 472      * @param listener
 473      *            The listener which should be notified.
 474      */
 475     public static void addDeviceChangeListener(IDeviceChangeListener listener) {
 476         synchronized (sLock) {
 477             if (sDeviceListeners.contains(listener) == false) {
 478                 sDeviceListeners.add(listener);
 479             }
 480         }
 481     }
 482
 483     /**
 484      * Removes the listener from the collection of listeners who will be
 485      * notified when a {@link IDevice} is connected, disconnected, or when its
 486      * properties or its {@link Client} list changed.
 487      *
 488      * @param listener
 489      *            The listener which should no longer be notified.
 490      */
 491     public static void removeDeviceChangeListener(IDeviceChangeListener listener) {
 492         synchronized (sLock) {
 493             sDeviceListeners.remove(listener);
 494         }
 495     }
 496
 497     /**
 498      * Adds the listener to the collection of listeners who will be notified
 499      * when a {@link Client} property changed, by sending it one of the messages
 500      * defined in the {@link IClientChangeListener} interface.
 501      *
 502      * @param listener
 503      *            The listener which should be notified.
 504      */
 505     public static void addClientChangeListener(IClientChangeListener listener) {
 506         synchronized (sLock) {
 507             if (sClientListeners.contains(listener) == false) {
 508                 sClientListeners.add(listener);
 509             }
 510         }
 511     }
 512
 513     /**
 514      * Removes the listener from the collection of listeners who will be
 515      * notified when a {@link Client} property changed.
 516      *
 517      * @param listener
 518      *            The listener which should no longer be notified.
 519      */
 520     public static void removeClientChangeListener(IClientChangeListener listener) {
 521         synchronized (sLock) {
 522             sClientListeners.remove(listener);
 523         }
 524     }
 525
 526     /**
 527      * Returns the devices.
 528      *
 529      * @see #hasInitialDeviceList()
 530      */
 531     public IDevice[] getDevices() {
 532         synchronized (sLock) {
 533             if (mDeviceMonitor != null) {
 534                 return mDeviceMonitor.getDevices();
 535             }
 536         }
 537
 538         return new IDevice[0];
 539     }
 540
 541     /**
 542      * Returns whether the bridge has acquired the initial list from adb after
 543      * being created.
 544      * <p/>
 545      * Calling {@link #getDevices()} right after
 546      * {@link #createBridge(String, boolean)} will generally result in an empty
 547      * list. This is due to the internal asynchronous communication mechanism
 548      * with <code>adb</code> that does not guarantee that the {@link IDevice}
 549      * list has been built before the call to {@link #getDevices()}.
 550      * <p/>
 551      * The recommended way to get the list of {@link IDevice} objects is to
 552      * create a {@link IDeviceChangeListener} object.
 553      */
 554     public boolean hasInitialDeviceList() {
 555         if (mDeviceMonitor != null) {
 556             return mDeviceMonitor.hasInitialDeviceList();
 557         }
 558
 559         return false;
 560     }
 561
 562     /**
 563      * Sets the client to accept debugger connection on the custom
 564      * "Selected debug port".
 565      *
 566      * @param selectedClient
 567      *            the client. Can be null.
 568      */
 569     public void setSelectedClient(Client selectedClient) {
 570         MonitorThread monitorThread = MonitorThread.getInstance();
 571         if (monitorThread != null) {
 572             monitorThread.setSelectedClient(selectedClient);
 573         }
 574     }
 575
 576     /**
 577      * Returns whether the {@link AndroidDebugBridge} object is still connected
 578      * to the adb daemon.
 579      */
 580     public boolean isConnected() {
 581         MonitorThread monitorThread = MonitorThread.getInstance();
 582         if (mDeviceMonitor != null && monitorThread != null) {
 583             return mDeviceMonitor.isMonitoring()
 584                     && monitorThread.getState() != State.TERMINATED;
 585         }
 586         return false;
 587     }
 588
 589     /**
 590      * Returns the number of times the {@link AndroidDebugBridge} object
 591      * attempted to connect to the adb daemon.
 592      */
 593     public int getConnectionAttemptCount() {
 594         if (mDeviceMonitor != null) {
 595             return mDeviceMonitor.getConnectionAttemptCount();
 596         }
 597         return -1;
 598     }
 599
 600     /**
 601      * Returns the number of times the {@link AndroidDebugBridge} object
 602      * attempted to restart the adb daemon.
 603      */
 604     public int getRestartAttemptCount() {
 605         if (mDeviceMonitor != null) {
 606             return mDeviceMonitor.getRestartAttemptCount();
 607         }
 608         return -1;
 609     }
 610
 611     /**
 612      * Creates a new bridge.
 613      *
 614      * @param osLocation
 615      *            the location of the command line tool
 616      * @throws InvalidParameterException
 617      */
 618     private AndroidDebugBridge(String osLocation)
 619             throws InvalidParameterException {
 620         if (osLocation == null || osLocation.length() == 0) {
 621             throw new InvalidParameterException();
 622         }
 623         mAdbOsLocation = osLocation;
 624
 625         checkAdbVersion();
 626     }
 627
 628     /**
 629      * Creates a new bridge not linked to any particular adb executable.
 630      */
 631     private AndroidDebugBridge() {
 632     }
 633
 634     /**
 635      * Queries adb for its version number and checks it against
 636      * {@link #MIN_VERSION_NUMBER} and {@link #MAX_VERSION_NUMBER}
 637      */
 638     private void checkAdbVersion() {
 639         // default is bad check
 640         mVersionCheck = false;
 641
 642         if (mAdbOsLocation == null) {
 643             return;
 644         }
 645
 646         try {
 647             String[] command = new String[2];
 648             command[0] = mAdbOsLocation;
 649             command[1] = "version"; //$NON-NLS-1$
 650             Log.d(DDMS,
 651                     String.format("Checking ‘%1$s version‘", mAdbOsLocation)); //$NON-NLS-1$
 652             Process process = Runtime.getRuntime().exec(command);
 653
 654             ArrayList<String> errorOutput = new ArrayList<String>();
 655             ArrayList<String> stdOutput = new ArrayList<String>();
 656             int status = grabProcessOutput(process, errorOutput, stdOutput,
 657                     true /* waitForReaders */);
 658
 659             if (status != 0) {
 660                 StringBuilder builder = new StringBuilder(
 661                         "‘adb version‘ failed!"); //$NON-NLS-1$
 662                 for (String error : errorOutput) {
 663                     builder.append(‘\n‘);
 664                     builder.append(error);
 665                 }
 666                 Log.logAndDisplay(LogLevel.ERROR, "adb", builder.toString());
 667             }
 668
 669             // check both stdout and stderr
 670             boolean versionFound = false;
 671             for (String line : stdOutput) {
 672                 versionFound = scanVersionLine(line);
 673                 if (versionFound) {
 674                     break;
 675                 }
 676             }
 677             if (!versionFound) {
 678                 for (String line : errorOutput) {
 679                     versionFound = scanVersionLine(line);
 680                     if (versionFound) {
 681                         break;
 682                     }
 683                 }
 684             }
 685
 686             if (!versionFound) {
 687                 // if we get here, we failed to parse the output.
 688                 Log.logAndDisplay(LogLevel.ERROR, ADB,
 689                         "Failed to parse the output of ‘adb version‘"); //$NON-NLS-1$
 690             }
 691
 692         } catch (IOException e) {
 693             Log.logAndDisplay(LogLevel.ERROR, ADB,
 694                     "Failed to get the adb version: " + e.getMessage()); //$NON-NLS-1$
 695         } catch (InterruptedException e) {
 696         } finally {
 697
 698         }
 699     }
 700
 701     /**
 702      * Scans a line resulting from ‘adb version‘ for a potential version number.
 703      * <p/>
 704      * If a version number is found, it checks the version number against what
 705      * is expected by this version of ddms.
 706      * <p/>
 707      * Returns true when a version number has been found so that we can stop
 708      * scanning, whether the version number is in the acceptable range or not.
 709      *
 710      * @param line
 711      *            The line to scan.
 712      * @return True if a version number was found (whether it is acceptable or
 713      *         not).
 714      */
 715     @SuppressWarnings("all")
 716     // With Eclipse 3.6, replace by @SuppressWarnings("unused")
 717     private boolean scanVersionLine(String line) {
 718         if (line != null) {
 719             Matcher matcher = sAdbVersion.matcher(line);
 720             if (matcher.matches()) {
 721                 int majorVersion = Integer.parseInt(matcher.group(1));
 722                 int minorVersion = Integer.parseInt(matcher.group(2));
 723                 int microVersion = Integer.parseInt(matcher.group(3));
 724
 725                 // check only the micro version for now.
 726                 if (microVersion < ADB_VERSION_MICRO_MIN) {
 727                     String message = String.format(
 728                             "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
 729                                     + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
 730                             majorVersion, minorVersion, ADB_VERSION_MICRO_MIN,
 731                             microVersion);
 732                     Log.logAndDisplay(LogLevel.ERROR, ADB, message);
 733                 } else if (ADB_VERSION_MICRO_MAX != -1
 734                         && microVersion > ADB_VERSION_MICRO_MAX) {
 735                     String message = String.format(
 736                             "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
 737                                     + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
 738                             majorVersion, minorVersion, ADB_VERSION_MICRO_MAX,
 739                             microVersion);
 740                     Log.logAndDisplay(LogLevel.ERROR, ADB, message);
 741                 } else {
 742                     mVersionCheck = true;
 743                 }
 744
 745                 return true;
 746             }
 747         }
 748         return false;
 749     }
 750
 751     /**
 752      * Starts the debug bridge.
 753      *
 754      * @return true if success.
 755      */
 756     boolean start() {
 757         if (mAdbOsLocation != null
 758                 && (mVersionCheck == false || startAdb() == false)) {
 759             return false;
 760         }
 761
 762         mStarted = true;
 763
 764         // now that the bridge is connected, we start the underlying services.
 765         mDeviceMonitor = new DeviceMonitor(this);
 766         mDeviceMonitor.start();
 767
 768         return true;
 769     }
 770
 771     /**
 772      * Kills the debug bridge, and the adb host server.
 773      *
 774      * @return true if success
 775      */
 776     boolean stop() {
 777         // if we haven‘t started we return false;
 778         if (mStarted == false) {
 779             return false;
 780         }
 781
 782         // kill the monitoring services
 783         mDeviceMonitor.stop();
 784         mDeviceMonitor = null;
 785
 786         if (stopAdb() == false) {
 787             return false;
 788         }
 789
 790         mStarted = false;
 791         return true;
 792     }
 793
 794     /**
 795      * Restarts adb, but not the services around it.
 796      *
 797      * @return true if success.
 798      */
 799     public boolean restart() {
 800         if (mAdbOsLocation == null) {
 801             Log.e(ADB,
 802                     "Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
 803             return false;
 804         }
 805
 806         if (mVersionCheck == false) {
 807             Log.logAndDisplay(LogLevel.ERROR, ADB,
 808                     "Attempting to restart adb, but version check failed!"); //$NON-NLS-1$
 809             return false;
 810         }
 811         synchronized (this) {
 812             stopAdb();
 813
 814             boolean restart = startAdb();
 815
 816             if (restart && mDeviceMonitor == null) {
 817                 mDeviceMonitor = new DeviceMonitor(this);
 818                 mDeviceMonitor.start();
 819             }
 820
 821             return restart;
 822         }
 823     }
 824
 825     /**
 826      * Notify the listener of a new {@link IDevice}.
 827      * <p/>
 828      * The notification of the listeners is done in a synchronized block. It is
 829      * important to expect the listeners to potentially access various methods
 830      * of {@link IDevice} as well as {@link #getDevices()} which use internal
 831      * locks.
 832      * <p/>
 833      * For this reason, any call to this method from a method of
 834      * {@link DeviceMonitor}, {@link IDevice} which is also inside a
 835      * synchronized block, should first synchronize on the
 836      * {@link AndroidDebugBridge} lock. Access to this lock is done through
 837      * {@link #getLock()}.
 838      *
 839      * @param device
 840      *            the new <code>IDevice</code>.
 841      * @see #getLock()
 842      */
 843     void deviceConnected(IDevice device) {
 844         // because the listeners could remove themselves from the list while
 845         // processing
 846         // their event callback, we make a copy of the list and iterate on it
 847         // instead of
 848         // the main list.
 849         // This mostly happens when the application quits.
 850         IDeviceChangeListener[] listenersCopy = null;
 851         synchronized (sLock) {
 852             listenersCopy = sDeviceListeners
 853                     .toArray(new IDeviceChangeListener[sDeviceListeners.size()]);
 854         }
 855
 856         // Notify the listeners
 857         for (IDeviceChangeListener listener : listenersCopy) {
 858             // we attempt to catch any exception so that a bad listener doesn‘t
 859             // kill our
 860             // thread
 861             try {
 862                 listener.deviceConnected(device);
 863             } catch (Exception e) {
 864                 Log.e(DDMS, e);
 865             }
 866         }
 867     }
 868
 869     /**
 870      * Notify the listener of a disconnected {@link IDevice}.
 871      * <p/>
 872      * The notification of the listeners is done in a synchronized block. It is
 873      * important to expect the listeners to potentially access various methods
 874      * of {@link IDevice} as well as {@link #getDevices()} which use internal
 875      * locks.
 876      * <p/>
 877      * For this reason, any call to this method from a method of
 878      * {@link DeviceMonitor}, {@link IDevice} which is also inside a
 879      * synchronized block, should first synchronize on the
 880      * {@link AndroidDebugBridge} lock. Access to this lock is done through
 881      * {@link #getLock()}.
 882      *
 883      * @param device
 884      *            the disconnected <code>IDevice</code>.
 885      * @see #getLock()
 886      */
 887     void deviceDisconnected(IDevice device) {
 888         // because the listeners could remove themselves from the list while
 889         // processing
 890         // their event callback, we make a copy of the list and iterate on it
 891         // instead of
 892         // the main list.
 893         // This mostly happens when the application quits.
 894         IDeviceChangeListener[] listenersCopy = null;
 895         synchronized (sLock) {
 896             listenersCopy = sDeviceListeners
 897                     .toArray(new IDeviceChangeListener[sDeviceListeners.size()]);
 898         }
 899
 900         // Notify the listeners
 901         for (IDeviceChangeListener listener : listenersCopy) {
 902             // we attempt to catch any exception so that a bad listener doesn‘t
 903             // kill our
 904             // thread
 905             try {
 906                 listener.deviceDisconnected(device);
 907             } catch (Exception e) {
 908                 Log.e(DDMS, e);
 909             }
 910         }
 911     }
 912
 913     /**
 914      * Notify the listener of a modified {@link IDevice}.
 915      * <p/>
 916      * The notification of the listeners is done in a synchronized block. It is
 917      * important to expect the listeners to potentially access various methods
 918      * of {@link IDevice} as well as {@link #getDevices()} which use internal
 919      * locks.
 920      * <p/>
 921      * For this reason, any call to this method from a method of
 922      * {@link DeviceMonitor}, {@link IDevice} which is also inside a
 923      * synchronized block, should first synchronize on the
 924      * {@link AndroidDebugBridge} lock. Access to this lock is done through
 925      * {@link #getLock()}.
 926      *
 927      * @param device
 928      *            the modified <code>IDevice</code>.
 929      * @see #getLock()
 930      */
 931     void deviceChanged(IDevice device, int changeMask) {
 932         // because the listeners could remove themselves from the list while
 933         // processing
 934         // their event callback, we make a copy of the list and iterate on it
 935         // instead of
 936         // the main list.
 937         // This mostly happens when the application quits.
 938         IDeviceChangeListener[] listenersCopy = null;
 939         synchronized (sLock) {
 940             listenersCopy = sDeviceListeners
 941                     .toArray(new IDeviceChangeListener[sDeviceListeners.size()]);
 942         }
 943
 944         // Notify the listeners
 945         for (IDeviceChangeListener listener : listenersCopy) {
 946             // we attempt to catch any exception so that a bad listener doesn‘t
 947             // kill our
 948             // thread
 949             try {
 950                 listener.deviceChanged(device, changeMask);
 951             } catch (Exception e) {
 952                 Log.e(DDMS, e);
 953             }
 954         }
 955     }
 956
 957     /**
 958      * Notify the listener of a modified {@link Client}.
 959      * <p/>
 960      * The notification of the listeners is done in a synchronized block. It is
 961      * important to expect the listeners to potentially access various methods
 962      * of {@link IDevice} as well as {@link #getDevices()} which use internal
 963      * locks.
 964      * <p/>
 965      * For this reason, any call to this method from a method of
 966      * {@link DeviceMonitor}, {@link IDevice} which is also inside a
 967      * synchronized block, should first synchronize on the
 968      * {@link AndroidDebugBridge} lock. Access to this lock is done through
 969      * {@link #getLock()}.
 970      *
 971      * @param device
 972      *            the modified <code>Client</code>.
 973      * @param changeMask
 974      *            the mask indicating what changed in the <code>Client</code>
 975      * @see #getLock()
 976      */
 977     void clientChanged(Client client, int changeMask) {
 978         // because the listeners could remove themselves from the list while
 979         // processing
 980         // their event callback, we make a copy of the list and iterate on it
 981         // instead of
 982         // the main list.
 983         // This mostly happens when the application quits.
 984         IClientChangeListener[] listenersCopy = null;
 985         synchronized (sLock) {
 986             listenersCopy = sClientListeners
 987                     .toArray(new IClientChangeListener[sClientListeners.size()]);
 988
 989         }
 990
 991         // Notify the listeners
 992         for (IClientChangeListener listener : listenersCopy) {
 993             // we attempt to catch any exception so that a bad listener doesn‘t
 994             // kill our
 995             // thread
 996             try {
 997                 listener.clientChanged(client, changeMask);
 998             } catch (Exception e) {
 999                 Log.e(DDMS, e);
1000             }
1001         }
1002     }
1003
1004     /**
1005      * Returns the {@link DeviceMonitor} object.
1006      */
1007     DeviceMonitor getDeviceMonitor() {
1008         return mDeviceMonitor;
1009     }
1010
1011     /**
1012      * Starts the adb host side server.
1013      *
1014      * @return true if success
1015      */
1016     synchronized boolean startAdb() {
1017         if (mAdbOsLocation == null) {
1018             Log.e(ADB,
1019                     "Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
1020             return false;
1021         }
1022
1023         Process proc;
1024         int status = -1;
1025
1026         try {
1027             String[] command = new String[2];
1028             command[0] = mAdbOsLocation;
1029             command[1] = "start-server"; //$NON-NLS-1$
1030             Log.d(DDMS, String.format(
1031                     "Launching ‘%1$s %2$s‘ to ensure ADB is running.", //$NON-NLS-1$
1032                     mAdbOsLocation, command[1]));
1033             ProcessBuilder processBuilder = new ProcessBuilder(command);
1034             if (DdmPreferences.getUseAdbHost()) {
1035                 String adbHostValue = DdmPreferences.getAdbHostValue();
1036                 if (adbHostValue != null && adbHostValue.length() > 0) {
1037                     // TODO : check that the String is a valid IP address
1038                     Map<String, String> env = processBuilder.environment();
1039                     env.put("ADBHOST", adbHostValue);
1040                 }
1041             }
1042             proc = processBuilder.start();
1043
1044             ArrayList<String> errorOutput = new ArrayList<String>();
1045             ArrayList<String> stdOutput = new ArrayList<String>();
1046             status = grabProcessOutput(proc, errorOutput, stdOutput, false /* waitForReaders */);
1047
1048         } catch (IOException ioe) {
1049             Log.d(DDMS, "Unable to run ‘adb‘: " + ioe.getMessage()); //$NON-NLS-1$
1050             // we‘ll return false;
1051         } catch (InterruptedException ie) {
1052             Log.d(DDMS, "Unable to run ‘adb‘: " + ie.getMessage()); //$NON-NLS-1$
1053             // we‘ll return false;
1054         }
1055
1056         if (status != 0) {
1057             Log.w(DDMS,
1058                     "‘adb start-server‘ failed -- run manually if necessary"); //$NON-NLS-1$
1059             return false;
1060         }
1061
1062         Log.d(DDMS, "‘adb start-server‘ succeeded"); //$NON-NLS-1$
1063
1064         return true;
1065     }
1066
1067     /**
1068      * Stops the adb host side server.
1069      *
1070      * @return true if success
1071      */
1072     private synchronized boolean stopAdb() {
1073         if (mAdbOsLocation == null) {
1074             Log.e(ADB,
1075                     "Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
1076             return false;
1077         }
1078
1079         Process proc;
1080         int status = -1;
1081
1082         try {
1083             String[] command = new String[2];
1084             command[0] = mAdbOsLocation;
1085             command[1] = "kill-server"; //$NON-NLS-1$
1086             proc = Runtime.getRuntime().exec(command);
1087             status = proc.waitFor();
1088         } catch (IOException ioe) {
1089             // we‘ll return false;
1090         } catch (InterruptedException ie) {
1091             // we‘ll return false;
1092         }
1093
1094         if (status != 0) {
1095             Log.w(DDMS, "‘adb kill-server‘ failed -- run manually if necessary"); //$NON-NLS-1$
1096             return false;
1097         }
1098
1099         Log.d(DDMS, "‘adb kill-server‘ succeeded"); //$NON-NLS-1$
1100         return true;
1101     }
1102
1103     /**
1104      * Get the stderr/stdout outputs of a process and return when the process is
1105      * done. Both <b>must</b> be read or the process will block on windows.
1106      *
1107      * @param process
1108      *            The process to get the ouput from
1109      * @param errorOutput
1110      *            The array to store the stderr output. cannot be null.
1111      * @param stdOutput
1112      *            The array to store the stdout output. cannot be null.
1113      * @param displayStdOut
1114      *            If true this will display stdout as well
1115      * @param waitforReaders
1116      *            if true, this will wait for the reader threads.
1117      * @return the process return code.
1118      * @throws InterruptedException
1119      */
1120     private int grabProcessOutput(final Process process,
1121             final ArrayList<String> errorOutput,
1122             final ArrayList<String> stdOutput, boolean waitforReaders)
1123             throws InterruptedException {
1124         assert errorOutput != null;
1125         assert stdOutput != null;
1126         // read the lines as they come. if null is returned, it‘s
1127         // because the process finished
1128         Thread t1 = new Thread("") { //$NON-NLS-1$
1129             @Override
1130             public void run() {
1131                 // create a buffer to read the stderr output
1132                 InputStreamReader is = new InputStreamReader(
1133                         process.getErrorStream());
1134                 BufferedReader errReader = new BufferedReader(is);
1135
1136                 try {
1137                     while (true) {
1138                         String line = errReader.readLine();
1139                         if (line != null) {
1140                             Log.e(ADB, line);
1141                             errorOutput.add(line);
1142                         } else {
1143                             break;
1144                         }
1145                     }
1146                 } catch (IOException e) {
1147                     // do nothing.
1148                 }
1149             }
1150         };
1151
1152         Thread t2 = new Thread("") { //$NON-NLS-1$
1153             @Override
1154             public void run() {
1155                 InputStreamReader is = new InputStreamReader(
1156                         process.getInputStream());
1157                 BufferedReader outReader = new BufferedReader(is);
1158
1159                 try {
1160                     while (true) {
1161                         String line = outReader.readLine();
1162                         if (line != null) {
1163                             Log.d(ADB, line);
1164                             stdOutput.add(line);
1165                         } else {
1166                             break;
1167                         }
1168                     }
1169                 } catch (IOException e) {
1170                     // do nothing.
1171                 }
1172             }
1173         };
1174
1175         t1.start();
1176         t2.start();
1177
1178         // it looks like on windows process#waitFor() can return
1179         // before the thread have filled the arrays, so we wait for both threads
1180         // and the
1181         // process itself.
1182         if (waitforReaders) {
1183             try {
1184                 t1.join();
1185             } catch (InterruptedException e) {
1186             }
1187             try {
1188                 t2.join();
1189             } catch (InterruptedException e) {
1190             }
1191         }
1192
1193         // get the return code from the process
1194         return process.waitFor();
1195     }
1196
1197     /**
1198      * Returns the singleton lock used by this class to protect any access to
1199      * the listener.
1200      * <p/>
1201      * This includes adding/removing listeners, but also notifying listeners of
1202      * new bridges, devices, and clients.
1203      */
1204     static Object getLock() {
1205         return sLock;
1206     }
1207
1208     /**
1209      * Instantiates sSocketAddr with the address of the host‘s adb process.
1210      */
1211     private static void initAdbSocketAddr() {
1212         try {
1213             int adb_port = determineAndValidateAdbPort();
1214             sHostAddr = InetAddress.getByName(ADB_HOST);
1215             sSocketAddr = new InetSocketAddress(sHostAddr, adb_port);
1216         } catch (UnknownHostException e) {
1217             // localhost should always be known.
1218         }
1219     }
1220
1221     /**
1222      * Determines port where ADB is expected by looking at an env variable.
1223      * <p/>
1224      * The value for the environment variable ANDROID_ADB_SERVER_PORT is
1225      * validated, IllegalArgumentException is thrown on illegal values.
1226      * <p/>
1227      *
1228      * @return The port number where the host‘s adb should be expected or
1229      *         started.
1230      * @throws IllegalArgumentException
1231      *             if ANDROID_ADB_SERVER_PORT has a non-numeric value.
1232      */
1233     private static int determineAndValidateAdbPort() {
1234         String adb_env_var;
1235         int result = ADB_PORT;
1236         try {
1237             adb_env_var = System.getenv(SERVER_PORT_ENV_VAR);
1238
1239             if (adb_env_var != null) {
1240                 adb_env_var = adb_env_var.trim();
1241             }
1242
1243             if (adb_env_var != null && adb_env_var.length() > 0) {
1244                 // C tools (adb, emulator) accept hex and octal port numbers, so
1245                 // need to accept
1246                 // them too.
1247                 result = Integer.decode(adb_env_var);
1248
1249                 if (result <= 0) {
1250                     String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$
1251                             + ": must be >=0, got " //$NON-NLS-1$
1252                             + System.getenv(SERVER_PORT_ENV_VAR);
1253                     throw new IllegalArgumentException(errMsg);
1254                 }
1255             }
1256         } catch (NumberFormatException nfEx) {
1257             String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$
1258                     + ": illegal value ‘" //$NON-NLS-1$
1259                     + System.getenv(SERVER_PORT_ENV_VAR) + "‘"; //$NON-NLS-1$
1260             throw new IllegalArgumentException(errMsg);
1261         } catch (SecurityException secEx) {
1262             // A security manager has been installed that doesn‘t allow access
1263             // to env vars.
1264             // So an environment variable might have been set, but we can‘t
1265             // tell.
1266             // Let‘s log a warning and continue with ADB‘s default port.
1267             // The issue is that adb would be started (by the forked process
1268             // having access
1269             // to the env vars) on the desired port, but within this process, we
1270             // can‘t figure out
1271             // what that port is. However, a security manager not granting
1272             // access to env vars
1273             // but allowing to fork is a rare and interesting configuration, so
1274             // the right
1275             // thing seems to be to continue using the default port, as forking
1276             // is likely to
1277             // fail later on in the scenario of the security manager.
1278             Log.w(DDMS,
1279                     "No access to env variables allowed by current security manager. " //$NON-NLS-1$
1280                             + "If you‘ve set ANDROID_ADB_SERVER_PORT: it‘s being ignored."); //$NON-NLS-1$
1281         }
1282         return result;
1283     }
1284
1285 }

AndroidDebugBridge

时间: 2024-10-12 03:55:27

【Android测试】【第三节】ADB——源码浅谈的相关文章

【Android测试】【第七节】Monkey——源码浅谈

前言 根据上一篇我们学会了Monkey的用法,知道了Monkey可以非常容易的模拟伪随机的模拟事件.也许有的时候我们想让他稍微智能化一些,例如只在某个屏幕范围产生伪随机事件,或者说是只对某些指定Activity进行操作,这样就需要我们对Monkey进行改良了.而改良必须去改Monkey的源码,因此本节课们就简单的说说Monkey的源码. 源码下载地址:https://code.google.com/p/android-source-browsing/source/browse/cmds/monk

源码浅谈(一):java中的 toString()方法

前言: toString()方法 相信大家都用到过,一般用于以字符串的形式返回对象的相关数据. 最近项目中需要对一个ArrayList<ArrayList<Integer>> datas  形式的集合处理. 处理要求把集合数据转换成字符串形式,格式为 :子集合1数据+"#"+子集合2数据+"#"+....+子集合n数据. 举例: 集合数据 :[[1,2,3],[2,3,5]]  要求转成为 "[1,2,3]#[2,3,5]"

adb源码编译

============问题描述============ 项目需要,需要将adb在linux下进行重新编译,编译工具链已经有了,android中adb源码在/system/core/adb目录下,现不知道怎么把Android.mk中工具链路径进行修改,求方案! ============解决方案1============ 故意弄错一点源码,这样就会打印出错误信息,这样就是会显示出使用的哪个工具链.替换就好. ============解决方案2============ 引用 1 楼 kangear 

Python:渗透测试开源项目【源码值得精读】

sql注入工具:sqlmap DNS安全监测:DNSRecon 暴力破解测试工具:patator XSS漏洞利用工具:XSSer Web服务器压力测试工具:HULK SSL安全扫描器:SSLyze 网络 Scapy: send, sniff and dissect and forge network packets. Usable interactively or as a library pypcap, Pcapy and pylibpcap: several different Python

Android 高级自定义Toast及源码解析

Toast概述 Toast的作用 不需要和用户交互的提示框. 更多参见官网:https://developer.android.com/guide/topics/ui/notifiers/toasts.html Toast的简单使用 Toast.makeText(MainActivity.this.getApplicationContext(),"沉迷学习,日渐消瘦",Toast.LENGTH_SHORT).show() 自定义Toast Toast customToast = new

Android Small插件化框架源码分析

Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github.com/wequick/Small 插件化的方案,说到底要解决的核心问题只有三个: 1.1 插件类的加载 这个问题的解决和其它插件化框架的解决方法差不多.Android的类是由DexClassLoader加载的,通过反射可以将插件包动态加载进去.Small的gradle插件生成的是.so包,在初始

Android应用安全开发之源码安全

Android应用安全开发之源码安全 gh0stbo · 2016/01/21 10:24 0x00 简介 Android apk很容易通过逆向工程进行反编译,从而是其代码完全暴露给攻击者,使apk面临破解,软件逻辑修改,插入恶意代码,替换广告商ID等风险.我们可以采用以下方法对apk进行保护. 0x01 混淆保护 混淆是一种用来隐藏程序意图的技术,可以增加代码阅读的难度,使攻击者难以全面掌控app内部实现逻辑,从而增加逆向工程和破解的难度,防止知识产权被窃取. 代码混淆技术主要做了如下的工作:

Android # 4.0.x(1-3) 源码 下载 编译

Android 4.0源码下载方法:repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1 官方下载页面:http://source.android.com/source/downloading.html Android SDK 4.0官方下载页面:http://developer.android.com/sdk/android-4.0.html android 4.0.3最新源码下载

Android应用Preference相关及源码浅析(Preference组件家族篇)

1 前言 前一篇(点我阅读前一篇<Android应用Preference相关及源码浅析(SharePreferences篇)>)我们讨论分析使用了Android的SharePreferences,相信看过的朋友都有了自己的感悟与理解,这一篇我们继续乘热打铁来说说SharePreferences的衍生品--Preference组件. 其实Preference组件大家一定不陌生,因为Android系统的Setting应用及我们市面上一些符合Android设计思想的应用的设置界面一般都会用它来实现,