Empfohlen: „Zusammenfassung der Android-Interviewfragen 2020 [Sammlung] “
Es gibt zwei häufig verwendete Klassenlader in Android, DexClassLoader und PathClassLoader, die beide von BaseDexClassLoader erben. Der Unterschied besteht darin, dass DexClassLoader beim Aufruf des übergeordneten Klassenkonstruktors einen zusätzlichen Parameter „OptimizedDirectory“ übergibt. Dieses Verzeichnis muss ein interner Speicherpfad sein und wird zum Zwischenspeichern von vom System erstellten Dex-Dateien verwendet. Der Parameter von PathClassLoader ist null und kann nur Dex-Dateien im internen Speicherverzeichnis laden. So können wir DexClassLoader zum Laden externer APK-Dateien verwenden, was auch die Grundlage vieler Plug-in-Technologien ist.
2. Service
2.1. Lebenszyklusdiagramm
Normalerweise gibt es zwei Möglichkeiten, den Dienst zu starten: startService und bindService.
2.2. startService-Lebenszyklus
onCreate
onStartCommand
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) { }
Wenn bei Android ein Speichermangel auftritt, kann es sein, dass der aktuell ausgeführte Dienst zerstört wird und der Dienst dann neu erstellt wird, wenn der Speicher ausreicht. Das Verhalten des Dienstes, der vom Android-System zwangsweise zerstört und neu erstellt wird, hängt davon ab auf der onStartCommand-Methode im Rückgabewert. Es gibt drei Arten von Rückgabewerten, die wir üblicherweise verwenden: START_NOT_STICKY
, START_STICKY
und START_REDELIVER_INTENT
. Diese drei Werte sind alle statische Konstanten im Service.
START_NOT_STICKY
Wenn START_NOT_STICKY zurückgegeben wird, bedeutet dies, dass der Dienst natürlich nicht neu erstellt wird, wenn der Prozess, der den Dienst ausführt, vom Android-System gewaltsam beendet wird Es wird für einen bestimmten Zeitraum beendet. Wenn startService erneut aufgerufen wird, wird der Dienst erneut instanziiert. Unter welchen Umständen ist es also angemessen, diesen Wert zurückzugeben? Wenn es keine Rolle spielt, wie oft die von einem unserer Dienste ausgeführte Arbeit unterbrochen wird oder dieser beendet und nicht sofort neu erstellt werden muss, wenn der Android-Speicher knapp ist, ist dieses Verhalten akzeptabel, dann können wir den Rückgabewert festlegen onStartCommand auf START_NOT_STICKY. Beispielsweise muss ein Dienst regelmäßig die neuesten Daten vom Server abrufen: Verwenden Sie einen Timer, um den Dienst alle angegebenen N Minuten zu starten und die neuesten Daten vom Server abzurufen. Wenn der onStartCommand des Dienstes ausgeführt wird, ist in dieser Methode ein Timer für N Minuten geplant, um den Dienst erneut zu starten und einen neuen Thread zur Durchführung von Netzwerkvorgängen zu öffnen. Unter der Annahme, dass der Dienst vom Android-System beim Abrufen der neuesten Daten vom Server zwangsweise beendet wird, wird der Dienst nicht neu erstellt. Dies spielt keine Rolle, da der Timer den Dienst erneut startet und die Daten erneut abruft Daten nach N Minuten.
START_STICKY
Wenn START_STICKY zurückgegeben wird, bedeutet dies, dass das Android-System den Dienst weiterhin in den gestarteten Zustand versetzt, nachdem der Prozess, der den Dienst ausführt, vom Android-System gewaltsam beendet wurde ( d. h. Ausführungsstatus), aber das von der onStartCommand-Methode übergebene Absichtsobjekt wird nicht mehr gespeichert, und dann versucht das Android-System, den Dienst erneut zu erstellen und die onStartCommand-Rückrufmethode auszuführen, aber der Intent-Parameter der onStartCommand-Rückrufmethode ist null Das heißt, obwohl die onStartCommand-Methode ausgeführt wird, können keine Informationen zur Absicht abgerufen werden. Wenn Ihr Dienst jederzeit problemlos ausgeführt oder beendet werden kann und keine Absichtsinformationen benötigt, können Sie START_STICKY in der onStartCommand-Methode zurückgeben. Beispielsweise eignet sich ein Dienst, der zum Abspielen von Hintergrundmusik verwendet wird, für die Rückgabe dieses Werts.
START_REDELIVER_INTENT
Wenn START_REDELIVER_INTENT zurückgegeben wird, bedeutet dies, dass das Android-System neu erstellt wird, nachdem der Prozess, der den Dienst ausführt, vom Android-System gewaltsam beendet wird, ähnlich wie bei der Rückgabe von START_STICKY Rufen Sie den Dienst erneut auf und führen Sie die Rückrufmethode onStartCommand aus. Der Unterschied besteht jedoch darin, dass das Android-System die Absicht behält, die zuletzt an die Methode onStartCommand übergeben wurde, bevor der Dienst beendet wurde, und sie erneut an die Methode onStartCommand des neu erstellten Dienstes übergibt wir Sie können die Absichtsparameter lesen. Solange START_REDELIVER_INTENT zurückgegeben wird, darf die Absicht in onStartCommand nicht null sein. Wenn unser Dienst für die Ausführung auf eine bestimmte Absicht angewiesen ist (relevante Dateninformationen aus der Absicht lesen muss usw.) und es nach der erzwungenen Zerstörung neu erstellt werden muss, ist ein solcher Dienst für die Rückgabe von START_REDELIVER_INTENT geeignet.
Die onBind-Methode in Service ist eine abstrakte Methode, daher ist die Service-Klasse selbst eine abstrakte Klasse, das heißt, die onBind-Methode muss neu geschrieben werden, auch wenn wir sie nicht verwenden Es. Wenn wir Service über startService verwenden, müssen wir beim Überschreiben der onBind-Methode nur null zurückgeben. Die onBind-Methode wird hauptsächlich beim Aufrufen von Service für die bindService-Methode verwendet.
onDestroy: Der über die startService-Methode gestartete Dienst wird auf unbestimmte Zeit ausgeführt. Nur wenn der StopService des Kontexts oder die stopSelf-Methode innerhalb des Dienstes aufgerufen wird, wird die Ausführung des Dienstes gestoppt und zerstört werden. Die Service-Callback-Funktion wird ausgeführt, wenn sie zerstört wird.
Das Starten des Dienstes über bindService hat hauptsächlich die folgenden Lebenszyklusfunktionen:
Das System ruft diese Methode auf, wenn der Dienst zum ersten Mal erstellt wird. Wenn der Dienst bereits ausgeführt wird, wird diese Methode nicht aufgerufen und nur einmal aufgerufen.
Diese Methode wird vom System aufgerufen, wenn eine andere Komponente den Start des Dienstes durch Aufruf von startService() anfordert.
Das System ruft diese Methode auf, wenn der Dienst nicht mehr verwendet wird und zerstört wird.
Das System ruft diese Methode auf, wenn eine andere Komponente durch Aufruf von bindService() eine Bindung an den Dienst herstellt.
Das System ruft diese Methode auf, wenn eine andere Komponente durch Aufruf von unbindService() die Bindung zum Dienst aufhebt.
Wenn die alte Komponente vom Dienst getrennt wird, eine andere neue Komponente an den Dienst gebunden wird und onUnbind() true zurückgibt, ruft das System diese Methode auf.
首先我们需要创建一个xml文件,然后创建与之对应的java文件,通过onCreatView()的返回方法进行关联,最后我们需要在Activity中进行配置相关参数即在Activity的xml文件中放上fragment的位置。
<fragment android:name="xxx.BlankFragment" android:layout_width="match_parent" android:layout_height="match_parent"> </fragment>
动态创建Fragment主要有以下几个步骤:
FragmnetPageAdapter在每次切换页面时,只是将Fragment进行分离,适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响。
FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适合页面较多的Fragment使用,这样就不会消耗更多的内存
Activity的生命周期如下图:
动态加载时,Activity的onCreate()调用完,才开始加载fragment并调用其生命周期方法,所以在第一个生命周期方法onAttach()中便能获取Activity以及Activity的布局的组件;
1.静态加载时,Activity的onCreate()调用过程中,fragment也在加载,所以fragment无法获取到Activity的布局中的组件,但为什么能获取到Activity呢?
2.原来在fragment调用onAttach()之前其实还调用了一个方法onInflate(),该方法被调用时fragment已经是和Activity相互结合了,所以可以获取到对方,但是Activity的onCreate()调用还未完成,故无法获取Activity的组件;
3.Activity的onCreate()调用完成是,fragment会调用onActivityCreated()生命周期方法,因此在这儿开始便能获取到Activity的布局的组件;
fragment不通过构造函数进行传值的原因是因为横屏切换的时候获取不到值。
Activity向Fragment传值,要传的值放到bundle对象里;
在Activity中创建该Fragment的对象fragment,通过调用setArguments()传递到fragment中;
在该Fragment中通过调用getArguments()得到bundle对象,就能得到里面的值。
在Activity中调用getFragmentManager()得到fragmentManager,,调用findFragmentByTag(tag)或者通过findFragmentById(id),例如:
FragmentManager fragmentManager = getFragmentManager(); Fragment fragment = fragmentManager.findFragmentByTag(tag);
通过回调的方式,定义一个接口(可以在Fragment类中定义),接口中有一个空的方法,在fragment中需要的时候调用接口的方法,值可以作为参数放在这个方法中,然后让Activity实现这个接口,必然会重写这个方法,这样值就传到了Activity中
通过findFragmentByTag得到另一个的Fragment的对象,这样就可以调用另一个的方法了。
通过接口回调的方式。
通过setArguments,getArguments的方式。
一种是add方式来进行show和add,这种方式你切换fragment不会让fragment重新刷新,只会调用onHiddenChanged(boolean isHidden)。
而用replace方式会使fragment重新刷新,因为add方式是将fragment隐藏了而不是销毁再创建,replace方式每次都是重新创建。
两者都可以提交fragment的操作,唯一的不同是第二种方法,允许丢失一些界面的状态和信息,几乎所有的开发者都遇到过这样的错误:无法在activity调用了onSaveInstanceState之后再执行commit(),这种异常时可以理解的,界面被系统回收(界面已经不存在),为了在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候我们再去修改界面理论上肯定是不允许的,所以为了避免这种异常,要使用第二种方法。
Wenn wir Fragmente häufig verwenden, wird es häufig in Verbindung mit Viewpager verwendet. Dann tritt ein Problem auf, das heißt, dass das Fragment beim Initialisieren in das Netzwerk aufgenommen wird Die von uns geschriebene Anfrage ist sehr leistungsintensiv. Der ideale Weg besteht darin, das Netzwerk nur dann anzufordern, wenn der Benutzer auf das aktuelle Fragment klickt oder dorthin wechselt. Deshalb haben wir den Begriff Lazy Loading erfunden.
Viewpager wird mit Fragmenten verwendet und die ersten beiden Fragmente werden standardmäßig geladen. Es kann leicht zu Problemen wie Netzwerkpaketverlust und Überlastung kommen.
Es gibt eine setUserVisibleHint-Methode in Fragment, und diese Methode ist besser als die onCreate()-Methode. Sie teilt uns mit, ob das aktuelle Fragment über isVisibleToUser sichtbar ist.
Aus dem Protokoll geht hervor, dass setUserVisibleHint() früher als onCreateView aufgerufen wird. Wenn Sie also Lazy Loading in setUserVisibleHint() implementieren möchten, müssen Sie sicherstellen, dass die View und andere Variablen initialisiert wurden, um dies zu vermeiden Nullzeiger.
Verwendungsschritte:
Deklarieren Sie eine Variable isPrepare=false, isVisible=false, die angibt, ob die aktuelle Seite erstellt wurde
Setzen Sie isPrepare=true während des onViewCreated-Zyklus
In setUserVisibleHint (boolean isVisible) Um zu bestimmen, ob angezeigt werden soll, setzen Sie isVisible=true
, um isPrepare und isVisible zu bestimmen. Beide sind true, um mit dem Laden der Daten zu beginnen, und stellen dann isPrepare und isVisible auf false wieder her, um wiederholtes Laden zu verhindern.
Informationen zum verzögerten Laden von Android-Fragmenten finden Sie unter folgendem Link: Verzögertes Laden von Fragmenten
Benutzer Klicken Sie im Launcher-Programm auf das Anwendungssymbol, um die Eintragsaktivität der Anwendung zu starten. Wenn die Aktivität gestartet wird, ist eine Interaktion zwischen mehreren Prozessen im Android-System erforderlich, die der Inkubation der Prozesse der Android-Framework-Ebene und -Anwendung gewidmet ist Layer-Programme. Es gibt auch einen system_server-Prozess, der viele Binder-Dienste ausführt. Beispielsweise werden ActivityManagerService, PackageManagerService und WindowManagerService ausgeführt. Diese Bindemitteldienste werden jeweils in unterschiedlichen Threads ausgeführt, wobei ActivityManagerService für die Verwaltung des Aktivitätsstapels, des Anwendungsprozesses und der Aufgabe verantwortlich ist.
Wenn der Benutzer auf das Anwendungssymbol im Launcher-Programm klickt, wird der ActivityManagerService benachrichtigt, die Eintragsaktivität der Anwendung zu starten, wenn der ActivityManagerService findet Wenn die Anwendung nicht gestartet wurde, benachrichtigt sie den Zygote-Prozess über den Hatch-Anwendungsprozess und führt dann die Hauptmethode von ActivityThread in diesem Dalvik-Anwendungsprozess aus. Der Anwendungsprozess benachrichtigt ActivityManagerService als Nächstes, dass der Anwendungsprozess gestartet wurde. ActivityManagerService speichert ein Proxy-Objekt des Anwendungsprozesses, sodass ActivityManagerService den Anwendungsprozess über dieses Proxy-Objekt steuern kann. Anschließend benachrichtigt ActivityManagerService den Anwendungsprozess, um eine Instanz der Eintragsaktivität zu erstellen und führen Sie seine Lebenszyklusmethode aus.
Android-Zeichenprozessfenster-Startprozessanalyse
4.2, Aktivitätslebenszyklus
Aktivität befindet sich im aktiven Zustand. Zu diesem Zeitpunkt befindet sich die Aktivität oben im Stapel, ist sichtbar und kann mit dem Benutzer interagieren.
Wenn die Aktivität den Fokus verliert oder durch eine neue Nicht-Vollbildaktivität oder durch eine transparente Aktivität oben auf dem Stapel platziert wird, wird die Aktivität in umgewandelt der Pausenzustand. Wir müssen jedoch verstehen, dass die Aktivität zu diesem Zeitpunkt nur die Fähigkeit zur Interaktion mit dem Benutzer verloren hat und alle ihre Statusinformationen und Mitgliedsvariablen noch vorhanden sind. Nur wenn der Systemspeicher knapp ist, kann sie vom System recycelt werden.
Wenn eine Aktivität vollständig von einer anderen Aktivität abgedeckt wird, wechselt die abgedeckte Aktivität in den Status „Gestoppt“. Zu diesem Zeitpunkt ist sie nicht mehr sichtbar, behält aber ihren Status als „Pausiert“. Zustand. Alle Statusinformationen und ihre Mitgliedsvariablen.
Wenn die Aktivität vom System recycelt wird, befindet sich die Aktivität im Status „Getötet“.
Die Aktivität wechselt zwischen den oben genannten vier Formen. Die Art und Weise des Wechsels hängt von der Bedienung des Benutzers ab. Nachdem wir die vier Formen der Aktivität verstanden haben, sprechen wir über den Lebenszyklus der Aktivität.
Der sogenannte typische Lebenszyklus besteht darin, dass die Aktivität unter Beteiligung des Benutzers den normalen Lebenszyklusprozess von der Erstellung über die Ausführung, das Anhalten, die Zerstörung usw. durchläuft.
Diese Methode wird beim Erstellen der Aktivität als erste aufgerufen. Im Allgemeinen müssen wir diese Methode beim Erstellen der Aktivität überschreiben Führen Sie einige Initialisierungsvorgänge in der Methode durch, z. B. das Festlegen von Ressourcen für das Schnittstellenlayout über setContentView, das Initialisieren erforderlicher Komponenteninformationen usw.
Wenn diese Methode zurückgerufen wird, bedeutet dies, dass die Aktivität zu diesem Zeitpunkt bereits sichtbar ist, aber noch nicht im Vordergrund angezeigt wurde Es kann nicht mit dem Benutzer interagieren. Es kann einfach verstanden werden, dass die Aktivität angezeigt wurde und wir sie nicht sehen können.
Wenn diese Methode zurückgerufen wird, bedeutet dies, dass die Aktivität im Vordergrund sichtbar ist und mit dem Benutzer interagieren kann (im oben erwähnten Status „Aktiv/Ausgeführt“). Die onResume-Methode weist zwei Ähnlichkeiten auf Mit onStart hat jeder gesagt, dass die Aktivität sichtbar ist, aber wenn onStart zurückgerufen wird, befindet sich die Aktivität immer noch im Hintergrund und kann nicht mit dem Benutzer interagieren, während onResume bereits im Vordergrund angezeigt wird und mit dem Benutzer interagieren kann. Aus dem Flussdiagramm können wir natürlich auch ersehen, dass beim Stoppen der Aktivität (onPause-Methode und onStop-Methode aufgerufen) auch die onResume-Methode aufgerufen wird, wenn sie in den Vordergrund zurückkehrt, sodass wir auch einige Ressourcen in onResume initialisieren können Methode, wie z. B. Neuinitialisierung Ressourcen, die in der onPause- oder onStop-Methode freigegeben werden.
Wenn diese Methode zurückgerufen wird, bedeutet dies, dass die Aktivität gestoppt wird (angehaltener Zustand). Unter normalen Umständen wird die onStop-Methode sofort zurückgerufen. Anhand des Flussdiagramms können wir jedoch auch eine Situation erkennen, in der die onResume-Methode direkt nach der Ausführung der onPause-Methode ausgeführt wird. Dies ist ein relativ extremes Phänomen. Dies kann auf die Benutzeroperation zurückzuführen sein, die dazu geführt hat, dass die aktuelle Aktivität zurückgezogen wird Hintergrund und kehren Sie dann schnell zu dieser Aktivität zurück. Zu diesem Zeitpunkt wird die onResume-Methode aufgerufen. Natürlich können wir in der onPause-Methode einige Datenspeicherungs- oder Animationsstopp- oder Ressourcenrecycling-Vorgänge durchführen, dies sollte jedoch nicht zu zeitaufwändig sein, da dies Auswirkungen auf die Anzeige der neuen Aktivität haben kann – nachdem die onPause-Methode ausgeführt wurde onResume der neuen Aktivitätsmethode wird ausgeführt.
wird im Allgemeinen direkt nach Abschluss der onPause-Methode ausgeführt, was anzeigt, dass die Aktivität kurz vor dem Stoppen steht oder vollständig abgedeckt ist (Formular „Gestoppt“) läuft nur im Hintergrund. Ebenso können einige Ressourcenfreigabevorgänge in der onStop-Methode durchgeführt werden (nicht zu zeitaufwändig).
zeigt an, dass die Aktivität neu gestartet wird. Wenn die Aktivität von unsichtbar zu sichtbar wechselt, wird diese Methode zurückgerufen. Diese Situation liegt im Allgemeinen vor, wenn der Benutzer eine neue Aktivität öffnet, die aktuelle Aktivität angehalten wird (onPause und onStop werden ausgeführt) und wenn der Benutzer dann zur aktuellen Aktivitätsseite zurückkehrt, wird die onRestart-Methode zurückgerufen.
Zu diesem Zeitpunkt wird die Aktivität zerstört. Dies ist auch die letzte Methode, die im Lebenszyklus ausgeführt wird. Im Allgemeinen können wir einige Recyclingarbeiten und die endgültige Ressourcenfreigabe durchführen diese Methode.
Hier kommen wir zu einer Zusammenfassung: Wenn die Aktivität startet, werden onCreate(), onStart() und onResume() nacheinander aufgerufen, und wenn die Aktivität in den Hintergrund tritt (unsichtbar). , klicken Sie auf „Startseite“ oder werden Sie vollständig von einer neuen Aktivität abgedeckt), onPause() und onStop() werden nacheinander aufgerufen. Wenn die Aktivität in den Vordergrund zurückkehrt (vom Desktop zur ursprünglichen Aktivität zurückkehrt oder nach dem Überschreiben zur ursprünglichen Aktivität zurückkehrt), werden onRestart (), onStart () und onResume () nacheinander aufgerufen. Wenn die Aktivität beendet und zerstört wird (klicken Sie auf die Schaltfläche „Zurück“), werden nacheinander onPause(), onStop() und onDestroy() aufgerufen. Zu diesem Zeitpunkt ist der gesamte Rückruf der Lebenszyklusmethode der Aktivität abgeschlossen. Schauen wir uns nun das vorherige Flussdiagramm noch einmal an. Es sollte ziemlich klar sein. Nun, das ist der gesamte typische Lebenszyklusprozess einer Aktivität.
Die Beziehung zwischen Androids Aktivität, PhoneWindow und DecorView kann durch das folgende Diagramm dargestellt werden:
Zum Beispiel gibt es die folgende Ansicht: DecorView ist die oberste Ebene der gesamten Window-Schnittstelle und hat nur ein untergeordnetes Element, LinearLayout. Stellt die gesamte Fensteroberfläche dar, einschließlich der Benachrichtigungsleiste, der Titelleiste und der Inhaltsanzeigeleiste. In LinearLayout gibt es zwei untergeordnete FrameLayout-Elemente.
DecorView ist eine Ansicht der obersten Ebene, die im Wesentlichen ein FrameLayout ist. Sie enthält zwei Teile, die Titelleiste und die Inhaltsleiste davon sind FrameLayout. Die Inhaltsspalten-ID ist Inhalt. Dies ist der Teil der Aktivität, in dem setContentView festgelegt wird. Schließlich wird das Layout mit der Inhalts-ID zum FrameLayout hinzugefügt.
Inhalt abrufen: ViewGroup content=findViewById(android.id.content)
Set-Ansicht abrufen: getChildAt(0).
Jede Aktivität enthält ein Fensterobjekt , das Window-Objekt wird normalerweise von PhoneWindow implementiert.
PhoneWindow: Legen Sie DecorView als Stammansicht des gesamten Anwendungsfensters fest, bei dem es sich um die Implementierungsklasse von Window handelt. Es ist das grundlegendste Fenstersystem in Android. Jede Aktivität erstellt ein PhoneWindow-Objekt, das die Schnittstelle für die Interaktion zwischen Aktivität und dem gesamten Ansichtssystem darstellt.
DecorView: Es handelt sich um eine Ansicht der obersten Ebene, die den spezifischen Inhalt darstellt, der im PhoneWindow angezeigt werden soll. DecorView ist der Vorfahre aller Ansichten in der aktuellen Aktivität. Sie stellt dem Benutzer nichts dar.
Der Ereignisverteilungsmechanismus von View kann durch die folgende Abbildung dargestellt werden:
Wie oben gezeigt, ist die Abbildung in drei Ebenen unterteilt: Von oben nach unten: Als nächstes folgen Activity, ViewGroup und View.
Der Ereignisverteilungsprozess von ViewGroup ist wahrscheinlich wie folgt: Wenn das ViewGroup-Abfangereignis der obersten Ebene, also onInterceptTouchEvent, true zurückgibt, wird das Ereignis zur Verarbeitung an den ViewGroup-OnTouchListener übergeben Wenn beides festgelegt ist, wird onTouch aufgerufen, andernfalls wird onTouchEvent aufgerufen. Wenn beides festgelegt ist, blockiert onTouchEvent. Wenn onClickerListener festgelegt ist, wird onClick aufgerufen Wenn Sie es nicht abfangen, wird das Ereignis an die untergeordnete Ansicht des Klickereignisses weitergeleitet, in der es sich befindet. Zu diesem Zeitpunkt wird das DispatchTouchEvent der Unteransicht aufgerufen
Ereignisverteilung der Ansicht
"dispatchTouchEvent -> onTouchEvent -> onTouchEvent" Der Unterschied zwischen "onTouchEvent" und "onTouch" hat Vorrang vor "onTouchEvent". nicht ausgeführt werden, und onClick wird nicht ausgeführt.
In der XML-Layoutdatei müssen unsere Parameter „layout_width“ und „layout_height“ keine bestimmten Größen schreiben, sondern „wrap_content“ oder „match_parent“. Diese beiden Einstellungen geben nicht die tatsächliche Größe an, aber die Ansicht, die wir auf dem Bildschirm zeichnen, muss eine bestimmte Breite und Höhe haben. Aus diesem Grund müssen wir die Größe selbst verwalten und festlegen. Natürlich bietet die View-Klasse eine Standardverarbeitung, aber wenn die Standardverarbeitung der View-Klasse nicht unseren Anforderungen entspricht, müssen wir die onMeasure-Funktion ~ neu schreiben.
Die onMeasure-Funktion ist eine int-Ganzzahl, die den Messmodus und die Größe enthält. Daten vom Typ „int“ belegen 32 Bit, und was Google implementiert, ist, dass die ersten 2 Bit der int-Daten zur Unterscheidung verschiedener Layoutmodi verwendet werden und die nächsten 30 Bit Größendaten speichern. Die Verwendung der Funktion
onMeasure ist wie folgt:
match_parent –>GENAU. Wie ist es zu verstehen? match_parent soll den gesamten verbleibenden Speicherplatz der übergeordneten Ansicht verwenden und den verbleibenden Speicherplatz der übergeordneten Ansicht bestimmen. Dies ist die in der Ganzzahl dieses Messmodus gespeicherte Größe.
wrap_content –>AT_MOST. So verstehen Sie: Wir möchten die Größe festlegen, um den Inhalt unserer Ansicht zu umschließen. Die Größe entspricht dann der Größe, die uns von der übergeordneten Ansicht als Referenz angegeben wurde. Solange diese Größe nicht überschritten wird, wird die spezifische Größe entsprechend festgelegt auf unsere Bedürfnisse.
Feste Größe (z. B. 100 dp) –>GENAU. Wenn der Benutzer die Größe angibt, müssen wir nicht mehr eingreifen. Natürlich ist die angegebene Größe die Hauptgröße.
2.4. Zeichnen von ViewGroup
Das Anpassen von ViewGroup ist nicht so einfach~, weil es sich nicht nur um sich selbst kümmern muss , und seine Unteransichten müssen ebenfalls berücksichtigt werden. Wir alle wissen, dass ViewGroup ein View-Container ist, der die untergeordnete Ansicht enthält und dafür verantwortlich ist, die untergeordnete Ansicht am angegebenen Ort zu platzieren.
ViewGroup和子View的大小算出来了之后,接下来就是去摆放了吧,具体怎么去摆放呢?这得根据你定制的需求去摆放了,比如,你想让子View按照垂直顺序一个挨着一个放,或者是按照先后顺序一个叠一个去放,这是你自己决定的。
已经知道怎么去摆放还不行啊,决定了怎么摆放就是相当于把已有的空间”分割”成大大小小的空间,每个空间对应一个子View,我们接下来就是把子View对号入座了,把它们放进它们该放的地方去。
自定义ViewGroup可以参考:Android自定义ViewGroup
Android的包文件APK分为两个部分:代码和资源,所以打包方面也分为资源打包和代码打包两个方面,这篇文章就来分析资源和代码的编译打包原理。
具体说来:
Android apk的安装过程主要氛围以下几步:
可以使用下面的图表示:
概念:Retrofit是一个基于RESTful的HTTP网络请求框架的封装,其中网络请求的本质是由OKHttp完成的,而Retrofit仅仅负责网络请求接口的封装。
原理:App应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数,Header、URL等信息,之后由OKHttp完成后续的请求,在服务器返回数据之后,OKHttp将原始的结果交给Retrofit,最后根据用户的需求对结果进行解析。
1.在retrofit中通过一个接口作为http请求的api接口
public interface NetApi { @GET("repos/{owner}/{repo}/contributors") Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo); }
2.创建一个Retrofit实例
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
3.调用api接口
NetApi repo = retrofit.create(NetApi.class); //第三步:调用网络请求的接口获取网络请求 retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("username", "path"); call.enqueue(new Callback<ResponseBody>() { //进行异步请求 @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { //进行异步操作 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { //执行错误回调方法 } });
retrofit执行的原理如下:
1.首先,通过method把它转换成ServiceMethod。
2.然后,通过serviceMethod,args获取到okHttpCall对象。
3.最后,再把okHttpCall进一步封装并返回Call对象。
首先,创建retrofit对象的方法如下:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
在创建retrofit对象的时候用到了build()方法,该方法的实现如下:
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); //设置kHttpClient } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); //设置默认回调执行器 } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); //返回新建的Retrofit对象 }
该方法返回了一个Retrofit对象,通过retrofit对象创建网络请求的接口的方式如下:
NetApi repo = retrofit.create(NetApi.class);
retrofit对象的create()方法的实现如下:‘
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); //直接调用该方法 } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); //通过平台对象调用该方法 } ServiceMethod serviceMethod = loadServiceMethod(method); //获取ServiceMethod对象 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //传入参数生成okHttpCall对象 return serviceMethod.callAdapter.adapt(okHttpCall); //执行okHttpCall } }); }
Picasso:120K
Glide:475K
Fresco:3.4M
Android-Universal-Image-Loader:162K
图片函数库的选择需要根据APP的具体情况而定,对于严重依赖图片缓存的APP,例如壁纸类,图片社交类APP来说,可以选择最专业的Fresco。对于一般的APP,选择Fresco会显得比较重,毕竟Fresco3.4M的体量摆在这。根据APP对图片的显示和缓存的需求从低到高,我们可以对以上函数库做一个排序。
Picasso < Android-Universal-Image-Loader < Glide < Fresco
Picasso: Es funktioniert am besten mit der Netzwerkbibliothek von Square, da Picasso den Caching-Teil der Netzwerkanforderung an die okhttp-Implementierung übergeben kann.
Glide: Es imitiert die API von Picasso und fügt viele Erweiterungen hinzu (z. B. GIF und andere Unterstützung). Das Standard-Bitmap-Format von Glide hat einen höheren Speicheraufwand als das Standard-ARGB_8888-Format von Picasso die volle Größe (cachet nur einen Typ), während Glide die gleiche Größe wie ImageView zwischenspeichert (d. h. 5656 und 128128 sind zwei Caches).
FBs Bildlade-Framework Fresco: Der größte Vorteil ist das Laden von Bitmaps unter 5,0 (mindestens 2,3). In Systemen unter 5.0 platziert Fresco Bilder in einem speziellen Speicherbereich (Ashmem-Bereich). Wenn das Bild nicht angezeigt wird, wird der belegte Speicher natürlich automatisch freigegeben. Dadurch wird die APP flüssiger und der durch die Bildspeichernutzung verursachte OOM wird reduziert. Warum soll es unter 5.0 liegen, weil das System es standardmäßig im Ashmem-Bereich speichert?
Glide kann alle Funktionen ausführen, die Picasso erreichen kann, aber die erforderlichen Einstellungen sind unterschiedlich. Allerdings ist die Größe von Picasso viel kleiner als die von Glide. Wenn die Netzwerkanforderung im Projekt selbst okhttp oder retrofit verwendet (im Wesentlichen ist es immer noch okhttp), wird empfohlen, Picasso zu verwenden, was viel kleiner ist (die Arbeit). des quadratischen Familieneimers). Der Vorteil von Glide sind große Bildströme wie GIF und Video. Wenn Sie Videoanwendungen wie Meipai und Aipai erstellen, wird die Verwendung empfohlen.
Die Speicheroptimierung von Fresco unter 5.0 ist sehr gut, aber der Preis ist, dass das Volumen auch sehr groß ist.
Aber die Verwendung ist auch etwas umständlich (kleiner Vorschlag). : he Diese Funktionen können nur mit einer integrierten ImageView implementiert werden, was mühsam zu verwenden ist. Wir ändern sie normalerweise entsprechend Fresco und verwenden direkt ihre Bitmap-Ebene)
Referenzlink: https://www.cnblogs.com/kunpengit/p/4001680.html
Gson ist derzeit der vielseitigste Json-Parser Gson, ein Artefakt, wurde ursprünglich von Google als Reaktion auf den internen Bedarf von Google entwickelt. Seit der Veröffentlichung der ersten Version im Mai 2008 wurde es jedoch von vielen Unternehmen oder Benutzern verwendet. Die Anwendung von Gson besteht hauptsächlich aus zwei Konvertierungsfunktionen: toJson und fromJson. Sie weist keine Abhängigkeiten auf und erfordert keine zusätzlichen Jars. Sie kann direkt auf dem JDK ausgeführt werden. Bevor Sie diese Art der Objektkonvertierung verwenden, müssen Sie den Objekttyp und seine Mitglieder erstellen, bevor Sie die JSON-Zeichenfolge erfolgreich in das entsprechende Objekt konvertieren können. Solange die Klasse Get- und Set-Methoden enthält, kann Gson komplexe JSON-Typen vollständig in Beans oder Beans in JSON konvertieren. Dies ist ein Artefakt der JSON-Analyse. Von der Funktionalität her ist Gson einwandfrei, die Leistung hinkt jedoch der von FastJson hinterher.
Fastjson ist ein leistungsstarker JSON-Prozessor, der in der Java-Sprache geschrieben und von Alibaba entwickelt wurde.
Keine Abhängigkeiten, keine Notwendigkeit für zusätzliche JARs und kann direkt auf dem JDK ausgeführt werden. Bei der Konvertierung komplexer Bean-Typen in Json kann es zu Problemen mit FastJson kommen. Referenztypen können zu Json-Konvertierungsfehlern führen und Referenzen müssen angegeben werden. FastJson verwendet einen Originalalgorithmus, um die Analysegeschwindigkeit auf das Äußerste zu erhöhen und übertrifft damit alle JSON-Bibliotheken.
Um den Vergleich der Json-Technologie zusammenzufassen: Googles Gson und Alibabas FastJson können bei der Auswahl von Projekten parallel verwendet werden. Wenn es nur um eine funktionale Anforderung und keine Leistungsanforderungen geht, können Sie Googles Gson verwenden Leistungsanforderungen: Sie können Gson zum Konvertieren von Beans in JSON verwenden, um die Genauigkeit der Daten sicherzustellen.
Referenzlink – Android-Komponentisierungslösung
Module für jedes Unternehmen nach der Komponentisierung kann eine separate APP sein (isModuleRun=false). Bei der Veröffentlichung des Pakets wird jedes Geschäftsmodul als lib-Abhängigkeit verwendet. Dies wird vollständig durch eine Variable im Root-Projekt gradle.properties gesteuert. Der Status von isModuleRun ist unterschiedlich, und die Ladeanwendung und AndroidManifest unterscheiden sich, um zu unterscheiden, ob es sich um eine unabhängige APK oder eine Bibliothek handelt.
Wenn wir mehrere Module erstellen, wie können dieselben Ressourcenkonflikte in der Datei gelöst werden? Das Zusammenführen von Namen führt zu Konflikten:
Um doppelte Ressourcennamen zu vermeiden, fügen Sie im Build jeder Komponente das Präfix „xxx_“ hinzu. um das Ressourcennamenpräfix in Gradle zu erzwingen. Der Wert „resourcePrefix“ kann jedoch nur die Ressourcen in XML begrenzen und nicht die Bildressourcen So referenzieren Sie einige gängige Bibliotheken und Toolklassen zwischen mehreren Modulen
Nach der Komponentisierung sind die Module voneinander isoliert, wie werden UI-Sprünge und Methoden ausgeführt? Zum Aufrufen können Sie Routing verwenden Frameworks wie Alibaba ARouter oder Meituans WMRouter
Das wissen wir Komponenten sind miteinander verbunden, also wie man die Parameter erhält, die von anderen Modulen übergeben werden, wenn man alleine debuggt
Anwendung
5.2, Plug-in
( 1) Übersicht
Wenn es um Plug-Ins geht, müssen wir das Problem erwähnen, dass die Anzahl der Methoden 65535 übersteigt. Wir können es durch Dex-Unterauftragsvergabe lösen, und wir können es auch durch die Verwendung von Plug-In-Entwicklung lösen . Das Konzept des Plug-Ins besteht darin, die Plug-In-APP zu laden und auszuführen (2 Vorteile)
Bildschirmgröße
Bedeutung: Anzahl der Pixel pro Zoll Einheit: dpi (Punkte pro Zoll)
Angenommen, das Gerät verfügt über 160 Pixel pro Zoll, dann beträgt die Bildschirmpixeldichte das Gerät =160dpi
1. Um die Flexibilität des Layouts zu gewährleisten und sich an verschiedene Bildschirmgrößen anzupassen Verwenden Sie „wrap_content“, „match_parent“, um die Breite und Höhe bestimmter Ansichtskomponenten zu steuern.
2. Relatives Layout verwenden und absolutes Layout deaktivieren.
3. Verwenden Sie das Weight-Attribut von LinearLayout
Wenn unsere Breite nicht 0dp ist (wrap_content und 0dp haben den gleichen Effekt), was ist mit match_parent?
Die wahre Bedeutung von android:layout_weight ist: Wenn die Ansicht dieses Attribut festlegt und es gültig ist, entspricht die Breite der Ansicht der ursprünglichen Breite (android:layout_width) plus dem Anteil des verbleibenden Platzes .
Aus dieser Perspektive erklären wir das obige Phänomen. Im obigen Code setzen wir die Breite jeder Schaltfläche auf match_parent. Angenommen, die Bildschirmbreite beträgt L, dann sollte die Breite jeder Schaltfläche auch L sein und die verbleibende Breite ist gleich L-(L+L) = -L .
Gewicht von Button1 = 1, das verbleibende Breitenverhältnis beträgt 1/(1+2)= 1/3, also ist die endgültige Breite L+1/3*(-L)=2/3L, Button2 ist The Die Berechnung ist ähnlich, die endgültige Breite beträgt L+2/3(-L)=1/3L.
4. Verwenden Sie .9-Bilder
7
Stabil (Speicherüberlauf, Absturz). )
Glatt (steckt fest)
Verbrauch (Stromverbrauch, Verkehr)
Installationspaket (APK-Verschlankung)
Es gibt viele Gründe, die die Stabilität beeinträchtigen, wie z. B. unangemessene Speichernutzung, unzureichende Berücksichtigung von Code-Ausnahmeszenarien , unangemessene Codelogik usw. wirken sich alle auf die Stabilität der Anwendung aus. Die beiden häufigsten Szenarien sind: Absturz und ANR. Diese beiden Fehler machen das Programm unbrauchbar. Machen Sie daher bei der globalen Überwachung von Abstürzen gute Arbeit, behandeln Sie Abstürze und sammeln und zeichnen Sie Absturzinformationen und Ausnahmeinformationen für die spätere Analyse auf. Verwenden Sie den Hauptthread, um das Geschäft ordnungsgemäß zu verarbeiten, und führen Sie keine zeitaufwändigen Vorgänge im Hauptthread aus verhindern, dass ANR-Programme nicht mehr reagieren.
(1) Stabilität – Speicheroptimierung
LeakCanary-Tool:
LeakCanary ist ein von Square entwickeltes Open-Source-Framework auf Basis von MAT zur Überwachung von Android-Speicherlecks. Das Arbeitsprinzip lautet:Der Überwachungsmechanismus verwendet Javas WeakReference und ReferenceQueue. Wenn das von WeakReference umschlossene Aktivitätsobjekt recycelt wird, wird die WeakReference-Referenz in die ReferenceQueue eingefügt ReferenceQueue kann verwendet werden, um zu überprüfen, ob die Aktivität recycelt werden kann (in der ReferenceQueue wird angegeben, dass sie recycelt werden kann und kein Leck vorliegt; andernfalls liegt möglicherweise ein Leck vor. LeakCanary führt GC einmal aus. Wenn es nicht in der ist ReferenceQueue, es wird als Leck betrachtet.
Wenn festgestellt wird, dass die Aktivität durchgesickert ist, greifen Sie auf die Speicherabbilddatei (Debug.dumpHprofData) zu. Analysieren Sie dann die Speicherdatei über HeapAnalyzer (checkForLeak – findLeakingReference – findLeakTrace). Analyse. Schließlich wird der Speicherverlust über DisplayLeakService angezeigt.
(3) Android Lint Tool:
(2) Glätte – Optimierung des Stotterns
Schnittstellenzeichnung: Der Hauptgrund ist, dass die Zeichnungsebene tief ist, die Seite komplex ist und die Aktualisierung unangemessen ist. Aus diesen Gründen werden in der Benutzeroberfläche, der ersten Schnittstelle nach dem Start, häufiger hängende Szenen angezeigt , und die Zeichnung, die zur Seite springt.
Datenverarbeitung: Der Grund für dieses Verzögerungsszenario ist, dass die Datenverarbeitung zu groß ist, was im Allgemeinen in drei Situationen unterteilt ist: Die eine ist, dass die Daten im UI-Thread verarbeitet werden, und die andere ist dass die Datenverarbeitung viel CPU beansprucht, was dazu führt, dass der Hauptthread nicht abgerufen werden kann. Drittens führt die Erhöhung des Speichers zu häufigem GC, was zu Verzögerungen führt.
Wenn das Android-System Ansichten misst, anordnet und zeichnet, durchläuft es die Anzahl der Ansichten. Wenn die Höhe einer Ansichtszahl zu hoch ist, wird die Geschwindigkeit der Messung, des Layouts und des Zeichnens erheblich beeinträchtigt. Google empfiehlt in seiner API-Dokumentation außerdem, dass die Ansichtshöhe 10 Ebenen nicht überschreiten sollte. In der aktuellen Version verwendet Google RelativeLayout anstelle von LineraLayout als Standard-Root-Layout. Der Zweck besteht darin, die Höhe des durch die LineraLayout-Verschachtelung generierten Layoutbaums zu reduzieren und so die Effizienz des UI-Renderings zu verbessern.
Layout wiederverwenden, Beschriftungen verwenden, um das Layout wiederzuverwenden;
Anzeigegeschwindigkeit erhöhen, verzögertes Laden der Ansicht verwenden;
Beachten Sie, dass die Verwendung von wrap_content das Maß erhöht Berechnen Sie die Kosten;
Löschen Sie nutzlose Attribute im Steuerelement
.×10***), ihre Darstellung ist 0,*****×10, in der Form .*** e ±**) im Computer stellt das Sternchen davor eine Festkomma-Dezimalzahl dar, also eine reine Dezimalzahl mit dem Der ganzzahlige Teil ist 0 und das Sternchen hinten. Der Exponententeil ist eine Festkomma-Ganzzahl. Jede ganze Zahl oder Dezimalzahl kann mit dieser Form ausgedrückt werden. Beispielsweise kann 1024 als 0,1024×10^4 ausgedrückt werden, was .1024e+004 ist, und 3,1415926 kann als 0,31415926×10^1 ausgedrückt werden, was .31415926e+001 ist , das ist eine Gleitkommazahl. Die an Gleitkommazahlen ausgeführten Operationen sind Gleitkommaoperationen. Gleitkommaoperationen sind komplexer als reguläre Operationen, daher führen Computer Gleitkommaoperationen viel langsamer aus als reguläre Operationen.
(2) Vermeiden Sie die unsachgemäße Verwendung von Wake Lock. Wake Lock ist ein Sperrmechanismus, der sich hauptsächlich auf den Ruhezustand des Systems bezieht. Solange jemand diese Sperre hält, kann das System nicht in den Ruhezustand versetzt werden Das System wird nicht schlafen. Der Zweck besteht darin, den Betrieb unseres Programms vollständig zu unterstützen. Wenn Sie dies nicht tun, treten in einigen Fällen Probleme auf. Beispielsweise stoppen Heartbeat-Pakete für Instant Messaging wie WeChat den Netzwerkzugriff kurz nach dem Ausschalten des Bildschirms. Daher wird Wake_Lock in WeChat häufig verwendet. Um Strom zu sparen, geht das System automatisch in den Ruhezustand, wenn die CPU nicht mit Aufgaben beschäftigt ist. Wenn es eine Aufgabe gibt, die die CPU für eine effiziente Ausführung aufwecken muss, wird der CPU ein Wake_Lock hinzugefügt. Ein häufiger Fehler, den jeder macht, ist, dass es leicht ist, die CPU zum Arbeiten aufzuwecken, aber leicht vergisst, Wake_Lock freizugeben. (3) Verwenden Sie Job Scheduler, um Hintergrundaufgaben zu verwalten. In Android 5.0 API 21 stellt Google eine Komponente namens JobScheduler API bereit, um das Szenario der Ausführung einer Aufgabe zu einem bestimmten Zeitpunkt oder wenn eine bestimmte Bedingung erfüllt ist, zu verarbeiten, z. B. wenn der Benutzer nachts ist Im Ruhezustand oder wenn das Gerät an das Netzteil angeschlossen und mit WLAN verbunden ist, beginnt es mit dem Herunterladen von Updates. Dies kann die Anwendungseffizienz verbessern und gleichzeitig den Ressourcenverbrauch reduzieren. (4) Installationspaket – APK-Verschlankung (1) Struktur des Installationspakets Assets-Ordner. Zum Speichern einiger Konfigurationsdateien und Ressourcendateien generieren Assets nicht automatisch entsprechende IDs, sondern erhalten diese über die Schnittstelle der AssetManager-Klasse. res. res ist die Abkürzung für Ressource. Die entsprechende ID wird automatisch generiert und der .R-Datei direkt zugeordnet. META-INF. Speichern Sie die Signaturinformationen der Anwendung, um die Integrität der APK-Datei zu überprüfen. AndroidManifest.xml. Diese Datei wird verwendet, um die Konfigurationsinformationen der Android-Anwendung, die Registrierungsinformationen einiger Komponenten, verwendbare Berechtigungen usw. zu beschreiben.classes.dex. Das Dalvik-Bytecode-Programm macht die virtuelle Dalvik-Maschine ausführbar. Im Allgemeinen verwenden Android-Anwendungen das dx-Tool im Android SDK, um beim Packen Java-Bytecode in Dalvik-Bytecode zu konvertieren.
resources.arsc. Es zeichnet die Zuordnungsbeziehung zwischen Ressourcendateien und Ressourcen-IDs auf und wird verwendet, um Ressourcen basierend auf Ressourcen-IDs zu finden.
Code-Verschleierung. Verwenden Sie das mit der IDE gelieferte Code-Obfuscator-Tool proGuard, das Komprimierung, Optimierung, Verschleierung und andere Funktionen umfasst.
Ressourcenoptimierung. Verwenden Sie beispielsweise Android Lint, um redundante Ressourcen zu löschen, Ressourcendateien zu minimieren usw.
Bildoptimierung. Verwenden Sie beispielsweise PNG-Optimierungstools, um Bilder zu komprimieren. Empfehlen Sie das fortschrittlichste Komprimierungstool Googlek, die Open-Source-Bibliothek Zopfli. Wenn die Anwendung Version 0 oder höher ist, wird empfohlen, das WebP-Bildformat zu verwenden.
Vermeiden Sie Bibliotheken von Drittanbietern mit doppelter oder nutzloser Funktionalität. Beispielsweise können Baidu Maps mit der Basiskarte verbunden werden, iFlytek Voice muss nicht offline verbunden werden, die Bildbibliothek GlidePicasso usw.
Plug-in-Entwicklung. Beispielsweise kann die Größe des Installationspakets reduziert werden, indem Funktionsmodule auf dem Server platziert und bei Bedarf heruntergeladen werden.
Sie können das Open-Source-Tool zur Dateiverschleierung von WeChat – AndResGuard – verwenden. Im Allgemeinen kann die APK-Größe auf etwa 1 MB komprimiert werden.
Referenzlink: https://www.jianshu.com/p/03c0fd3fc245
Kaltstart
Beim Starten einer Anwendung gibt es keinen Prozess für die Anwendung. Zu diesem Zeitpunkt erstellt das System einen neuen Prozess und weist ihn der Anwendung zu
Unterschied
Kaltstartprozess
Kaltstartoptimierung
8.1 MVP-Architektur Entwickelt aus MVC. In MVP steht M für Model, V für View und P für Presenter.
Modellschicht (Modell): Wird hauptsächlich zum Abrufen von Datenfunktionen, Geschäftslogik und Entitätsmodellen verwendet.
In MVP trennt die Presenter-Ebene die Ansichtsebene und die Modellebene vollständig und implementiert die Hauptprogrammlogik in der Presenter-Ebene. Der Presenter steht nicht in direktem Zusammenhang mit der spezifischen Ansichtsebene (Aktivität), ist jedoch vorhanden implementiert durch Definieren einer Schnittstelle für die Interaktion, sodass der Präsentator bei Änderungen der Ansichtsebene (Aktivität) weiterhin unverändert bleiben kann. Die Schnittstellenklasse der Ansichtsschicht sollte nur über Set/Get-Methoden verfügen und einige Schnittstelleninhalte und Benutzereingaben sollten nicht überflüssig sein. Der Ansichtsschicht ist es niemals gestattet, direkt auf die Modellschicht zuzugreifen. Dies ist der größte Unterschied zu MVC und der Hauptvorteil von MVP.
9. Virtuelle Maschine
9.1. Vergleich zwischen der virtuellen Maschine von Android Dalvik und der virtuellen Maschine von ART
Dalvik
Android 4.4 und früher verwenden die virtuelle Maschine von Dalvik. Wir wissen, dass Apk während des Verpackungsprozesses zunächst Java und andere Quellcodes über Javac in .class-Dateien kompiliert, unsere virtuelle Dalvik-Maschine jedoch nur .dex-Dateien ausführt. Zu diesem Zeitpunkt konvertiert dx die .class-Dateien in virtuelle Dalvik Maschinen. .dex-Datei für die Maschinenausführung. Wenn die virtuelle Dalvik-Maschine gestartet wird, konvertiert sie zunächst die .dex-Datei in einen schnell laufenden Maschinencode. Aufgrund des Problems von 65535 haben wir beim Kaltstart der Anwendung einen Co-Packing-Prozess Die App startet langsam. Dies ist die JIT-Funktion (Just In Time) der virtuellen Dalvik-Maschine.
App-Kaltstartgeschwindigkeit beschleunigen
提供功能全面的Debug特性
ART缺点:
APP安装速度慢,因为在APK安装的时候要生成可运行.oat文件
APK占用空间大,因为在APK安装的时候要生成可运行.oat文件
arm处理器
关于ART更详细的介绍,可以参考Android ART详解
熟悉Android性能分析工具、UI卡顿、APP启动、包瘦身和内存性能优化
熟悉Android APP架构设计,模块化、组件化、插件化开发
熟练掌握Java、设计模式、网络、多线程技术
jvm将.class类文件信息加载到内存并解析成对应的class对象的过程,注意:jvm并不是一开始就把所有的类加载进内存中,只是在第一次遇到某个需要运行的类才会加载,并且只加载一次
主要分为三部分:1、加载,2、链接(1.验证,2.准备,3.解析),3、初始化
类加载器包括 BootClassLoader、ExtClassLoader、APPClassLoader
验证:(验证class文件的字节流是否符合jvm规范)
准备:为类变量分配内存,并且进行赋初值
解析:将常量池里面的符号引用(变量名)替换成直接引用(内存地址)过程,在解析阶段,jvm会把所有的类名、方法名、字段名、这些符号引用替换成具体的内存地址或者偏移量。
主要对类变量进行初始化,执行类构造器的过程,换句话说,只对static修试的变量或者语句进行初始化。
范例:Person person = new Person();为例进行说明。
Java编程思想中的类的初始化过程主要有以下几点:
StringBuffer里面的很多方法添加了synchronized关键字,是可以表征线程安全的,所以多线程情况下使用它。
执行速度:
StringBuilder > StringBuffer > String
StringBuilder牺牲了性能来换取速度的,这两个是可以直接在原对象上面进行修改,省去了创建新对象和回收老对象的过程,而String是字符串常量(final)修试,另外两个是字符串变量,常量对象一旦创建就不可以修改,变量是可以进行修改的,所以对于String字符串的操作包含下面三个步骤:
Java对象实例化过程中,主要使用到虚拟机栈、Java堆和方法区。Java文件经过编译之后首先会被加载到jvm方法区中,jvm方法区中很重的一个部分是运行时常量池,用以存储class文件类的版本、字段、方法、接口等描述信息和编译期间的常量和静态常量。
类加载器classLoader,在JVM启动时或者类运行时将需要的.class文件加载到内存中。
执行引擎,负责执行class文件中包含的字节码指令。
本地方法接口,主要是调用C/C++实现的本地方法及返回结果。
内存区域(运行时数据区),是在JVM运行的时候操作所分配的内存区,
主要分为以下五个部分,如下图:
https://www.jianshu.com/nb/12554212
垃圾收集器一般完成两件事
通常,Java对象的引用可以分为4类:强引用、软引用、弱引用和虚引用。
强引用:通常可以认为是通过new出来的对象,即使内存不足,GC进行垃圾收集的时候也不会主动回收。
Object obj = new Object();
软引用:在内存不足的时候,GC进行垃圾收集的时候会被GC回收。
Object obj = new Object(); SoftReference<Object> softReference = new SoftReference<>(obj);
弱引用:无论内存是否充足,GC进行垃圾收集的时候都会回收。
Object obj = new Object(); WeakReference<Object> weakReference = new WeakReference<>(obj);
虚引用:和弱引用类似,主要区别在于虚引用必须和引用队列一起使用。
Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);
引用队列:如果软引用和弱引用被GC回收,JVM就会把这个引用加到引用队列里,如果是虚引用,在回收前就会被加到引用队列里。
引用计数法:给每个对象添加引用计数器,每个地方引用它,计数器就+1,失效时-1。如果两个对象互相引用时,就导致无法回收。
可达性分析算法:以根集对象为起始点进行搜索,如果对象不可达的话就是垃圾对象。根集(Java栈中引用的对象、方法区中常量池中引用的对象、本地方法中引用的对象等。JVM在垃圾回收的时候,会检查堆中所有对象是否被这些根集对象引用,不能够被引用的对象就会被垃圾回收器回收。)
常见的垃圾回收算法有:
标记:首先标记所有需要回收的对象,在标记完成之后统计回收所有被标记的对象,它的标记过程即为上面的可达性分析算法。
清除:清除所有被标记的对象
缺点:
效率不足,标记和清除效率都不高
空间问题,标记清除之后会产生大量不连续的内存碎片,导致大对象分配无法找到足够的空间,提前进行垃圾回收。
复制回收算法
将可用的内存按容量划分为大小相等的2块,每次只用一块,当这一块的内存用完了,就将存活的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。
缺点:
将内存缩小了原本的一般,代价比较高
大部分对象是“朝生夕灭”的,所以不必按照1:1的比例划分。
现在商业虚拟机采用这种算法回收新生代,但不是按1:1的比例,而是将内存区域划分为eden 空间、from 空间、to 空间 3 个部分。
其中 from 空间和 to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。
在垃圾回收时,eden 空间中的存活对象会被复制到未使用的 survivor 空间中 (假设是 to),正在使用的 survivor 空间 (假设是 from) 中的年轻对象也会被复制到 to 空间中 (大对象,或者老年对象会直接进入老年带,如果 to 空间已满,则对象也会直接进入老年代)。此时,eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空,to 空间则存放此次回收后的存活对象。这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。
在老年代的对象大都是存活对象,复制算法在对象存活率教高的时候,效率就会变得比较低。根据老年代的特点,有人提出了“标记-压缩算法(Mark-Compact)”
标记过程与标记-清除的标记一样,但后续不是对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。
这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
根据对象存活的周期不同将内存划分为几块,一般是把Java堆分为老年代和新生代,这样根据各个年代的特点采用适当的收集算法。
新生代每次收集都有大量对象死去,只有少量存活,那就选用复制算法,复制的对象数较少就可完成收集。
老年代对象存活率高,使用标记-压缩算法,以提高垃圾回收效率。
程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。
每个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但是可以用做其他ClassLoader实例的父类加载器。
当一个ClassLoader 实例需要加载某个类时,它会试图在亲自搜索这个类之前先把这个任务委托给它的父类加载器,这个过程是由上而下依次检查的,首先由顶层的类加载器Bootstrap CLassLoader进行加载,如果没有加载到,则把任务转交给Extension CLassLoader视图加载,如果也没有找到,则转交给AppCLassLoader进行加载,还是没有的话,则交给委托的发起者,由它到指定的文件系统或者网络等URL中进行加载类。还没有找到的话,则会抛出CLassNotFoundException异常。否则将这个类生成一个类的定义,并将它加载到内存中,最后返回这个类在内存中的Class实例对象。
JVM在判断两个class是否相同时,不仅要判断两个类名是否相同,还要判断是否是同一个类加载器加载的。
避免重复加载,父类已经加载了,则子CLassLoader没有必要再次加载。
考虑安全因素,假设自定义一个String类,除非改变JDK中CLassLoader的搜索类的默认算法,否则用户自定义的CLassLoader如法加载一个自己写的String类,因为String类在启动时就被引导类加载器Bootstrap CLassLoader加载了。
关于Android的双亲委托机制,可以参考android classloader双亲委托模式
Java集合类主要由两个接口派生出:Collection和Map,这两个接口是Java集合的根接口。
Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是 Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。
List,Set都是继承自Collection接口,Map则不是;
List特点:元素有放入顺序,元素可重复; Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法;
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;
HashMap是非线程安全的,HashTable是线程安全的;
Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
Vector可以设置增长因子,而ArrayList不可以。
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
HashSet底层通过HashMap来实现的,在往HashSet中添加元素是
public boolean add(E e) { return map.put(e, PRESENT)==null; } // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
在HashMap中进行查找是否存在这个key,value始终是一样的,主要有以下几种情况:
HashMap 非线程安全,基于哈希表(散列表)实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。其中散列表的冲突处理主要分两种,一种是开放定址法,另一种是链表法。HashMap的实现中采用的是链表法。
TreeMap:非线程安全基于红黑树实现,TreeMap没有调优选项,因为该树总处于平衡状态
当数值范围为-128~127时:如果两个new出来Integer对象,即使值相同,通过“”比较结果为false,但两个对象直接赋值,则通过“”比较结果为“true,这一点与String非常相似。
当数值不在-128~127时,无论通过哪种方式,即使两个对象的值相等,通过“”比较,其结果为false;
当一个Integer对象直接与一个int基本数据类型通过“”比较,其结果与第一点相同;
Integer对象的hash值为数值本身;
@Override public int hashCode() { return Integer.hashCode(value); }
在Integer类中有一个静态内部类IntegerCache,在IntegerCache类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException。
使用Java的泛型时应注意以下几点:
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
限定通配符对类型进行了限制。
Einer ist erweitert T>, der die Obergrenze des Typs festlegt, indem er sicherstellt, dass der Typ eine Unterklasse von T sein muss.
Der andere ist Typ, indem sichergestellt wird, dass der Typ eine Unterklasse der T-Elternklasse sein muss, um die untere Grenze des Typs festzulegen.
Andererseits stellt > einen unqualifizierten Platzhalter dar, da >
Liste erweitert Nummer> kann beispielsweise Liste oder Liste akzeptieren.
Für alle, die sich mit Generika nicht auskennen, mag diese Frage zu Java-Generika verwirrend erscheinen, denn auf den ersten Blick ist String eine Art Objekt, daher sollte List überall dort verwendet werden, wo List benötigt wird, aber das ist nicht der Fall Fall. Dies führt zu Kompilierungsfehlern. Wenn Sie weiter darüber nachdenken, werden Sie feststellen, dass dies für Java sinnvoll ist, da List jede Art von Objekt speichern kann, einschließlich String, Integer usw., List jedoch nur zum Speichern von Strings.
Array unterstützt eigentlich keine Generika, weshalb Joshua Bloch im Buch „Effective Java“ die Verwendung von List anstelle von Array vorgeschlagen hat, da List die Kompilierungszeit bereitstellen kann Typsicherheitsgarantien, Array dagegen nicht.
Der Hauptunterschied zwischen primitiven Typen und parametrisierten Typen besteht darin, dass der Compiler die primitiven Typen zur Kompilierungszeit nicht auf Typsicherheit prüft , aber der Typ mit Parametern wird überprüft. Indem Sie Object als Typ verwenden, können Sie dem Compiler mitteilen, dass die Methode jeden Objekttyp akzeptieren kann, z. B. String oder Integer. Der Testpunkt dieser Frage liegt im richtigen Verständnis primitiver Typen in Generika. Der zweite Unterschied zwischen ihnen besteht darin, dass Sie jeden Typ mit Parametern an den primitiven Typ List übergeben können, aber Sie können keine List an eine Methode übergeben, die eine List akzeptiert, da sonst ein Kompilierungsfehler auftritt.
Liste> ist eine Liste unbekannten Typs, und Liste ist eigentlich eine Liste beliebigen Typs. Sie können Liste, Liste zu Liste> zuweisen, aber Sie können keine Liste zu Liste zuweisen.
Für jede Klasse kann sie alle Eigenschaften und Methoden kennen. Für jedes Objekt kann jede seiner Methoden aufgerufen werden. Diese dynamische Erfassung von Informationen und die Funktion des dynamischen Aufrufs der Methoden des Objekts werden als Reflexionsmechanismus der Java-Sprache bezeichnet.
Der Java-Reflektionsmechanismus bietet hauptsächlich die folgenden Funktionen: Bestimmen Sie die Klasse, zu der ein Objekt zur Laufzeit gehört Mitgliedsvariablen und Methoden einer Klasse; Aufrufen von Methoden eines beliebigen Objekts zur Laufzeit;
Jeder muss mit dem Wort Agent vertraut sein, da es in der Realität häufig verwendet wird. Tatsächlich können Dinge in der Realität den abstrakten Prozess sehr anschaulich und intuitiv widerspiegeln Muster und Natur. Ist das Haus jetzt nicht sehr laut? Nehmen wir ein Haus als Beispiel, um den Schleier der Entscheidungsfreiheit zu lüften.
Angenommen, Sie möchten ein Haus verkaufen, indem Sie die Verkaufsinformationen direkt online veröffentlichen und dann die Personen, die das Haus kaufen möchten, direkt dorthin bringen, um das Haus zu besichtigen, den Eigentumstitel zu übertragen usw. bis zum Das Haus ist verkauft, aber es kann für Sie schwierig sein, dies zu tun. Wenn Sie beschäftigt sind und keine Zeit haben, sich mit diesen Dingen zu befassen, können Sie sich an einen Vermittler wenden und sich von diesem bei der Abwicklung dieser trivialen Dinge helfen lassen. Der Vermittler ist eigentlich Ihr Agent. Ursprünglich mussten Sie das tun, aber jetzt hilft Ihnen der Vermittler einzeln bei der Abwicklung. Für den Käufer gibt es keinen Unterschied zwischen direkten Transaktionen mit Ihnen und direkten Transaktionen mit dem Vermittler Das ist eigentlich ein Teil der Agentur.
Lassen Sie uns als Nächstes ausführlich darüber nachdenken, warum Sie ein Haus nicht direkt kaufen, sondern einen Makler benötigen. Tatsächlich beantwortet eine Frage genau die Frage, wann der Proxy-Modus verwendet werden sollte.
Grund 1: Sie arbeiten möglicherweise außerhalb der Stadt und Hauskäufer können Sie für direkte Transaktionen nicht finden.
Entsprechend unserem Programmdesign gilt: Der Client kann das eigentliche Objekt nicht direkt bedienen. Warum kann das nicht direkt gemacht werden? Eine Situation besteht darin, dass sich das Objekt, das Sie aufrufen müssen, auf einem anderen Computer befindet und Sie über das Netzwerk darauf zugreifen müssen. Wenn Sie es direkt aufrufen, müssen Sie sich um Netzwerkverbindungen, Verpackung, Entpacken und andere sehr komplizierte Schritte kümmern. Um die Verarbeitung des Clients zu vereinfachen, verwenden wir den Proxy-Modus, um einen Proxy für das Remote-Objekt auf dem Client einzurichten. Der Client ruft den Proxy auf, genau wie beim Aufrufen des lokalen Objekts, und dann kontaktiert der Proxy das eigentliche Objekt Beim Client gibt es möglicherweise keine. Es fühlt sich an, als ob sich das aufgerufene Ding am anderen Ende des Netzwerks befindet. So funktioniert der Webdienst tatsächlich. In einem anderen Fall haben Sie Angst, Ihren normalen Betrieb zu beeinträchtigen, obwohl es sich bei dem Objekt, das Sie anrufen möchten, um ein lokales Objekt handelt Um zu verstehen, dass in Word ein großes Bild installiert ist, müssen wir den Inhalt laden und gemeinsam öffnen. Wenn der Benutzer jedoch wartet, bis das große Bild geladen ist, ist der Benutzer möglicherweise bereits gesprungen in der Warteschleife, damit wir einen Proxy einrichten und den Proxy das Bild langsam öffnen lassen können, ohne die ursprüngliche Öffnungsfunktion von Word zu beeinträchtigen. Lassen Sie mich klarstellen, dass ich nur vermutet habe, dass Word dies tun könnte. Ich weiß nicht genau, wie es gemacht wird.
Grund 2: Sie wissen nicht, wie Sie die Transferverfahren durchführen sollen, oder Sie müssen zusätzlich zu dem, was Sie jetzt tun können, noch andere Dinge tun, um Ihr Ziel zu erreichen.
Entspricht unserem Programmdesign: Zusätzlich zu den Funktionen, die die aktuelle Klasse bereitstellen kann, müssen wir auch einige andere Funktionen hinzufügen. Die einfachste Situation ist die Berechtigungsfilterung. Ich habe eine Klasse, die ein bestimmtes Geschäft ausführt, aber aus Sicherheitsgründen können nur bestimmte Benutzer diese Klasse aufrufen. Zu diesem Zeitpunkt können wir eine Proxy-Klasse dieser Klasse erstellen, die alle Anforderungen erfordert Durchlaufen Diese Proxy-Klasse führt eine Berechtigungsbeurteilung durch. Wenn dies sicher ist, wird das Geschäft der tatsächlichen Klasse aufgerufen, um mit der Verarbeitung zu beginnen. Manche Leute fragen sich vielleicht, warum ich eine zusätzliche Proxy-Klasse hinzufügen muss? Ich muss nur die Berechtigungsfilterung zur Methode der ursprünglichen Klasse hinzufügen, oder? Bei der Programmierung gibt es ein Problem des Einheitsprinzips von Klassen. Dieses Prinzip ist sehr einfach, das heißt, die Funktion jeder Klasse ist so einheitlich wie möglich. Warum sollte es Single sein? Da nur eine Klasse mit einer einzigen Funktion am wenigsten geändert wird. Wenn Sie das Berechtigungsurteil in die aktuelle Klasse einfügen, muss die aktuelle Klasse für ihre eigene Geschäftslogik verantwortlich sein , Auch für die Berechtigungsbeurteilung verantwortlich, gibt es zwei Gründe für die Änderung dieser Klasse. Wenn sich nun die Berechtigungsregeln ändern, muss diese Klasse offensichtlich nicht geändert werden.
Okay, ich habe fast über die Prinzipien gesprochen. Wenn ich noch lange darüber rede, werden vielleicht alle mit Ziegeln werfen. Haha, mal sehen, wie wir als nächstes die Agentur implementieren.
https://zhuanlan.zhihu.com/p/27005757?utm_source=weibo&utm_medium=social
http://crazyandcoder.tech/2016 /09/14/Android-Algorithmus und Datenstruktur – Sortierung/
1.1. Direkte Einfügungssortierung
Sortieren Sie die erste Zahl und die zweite Zahl und bilden Sie dann eine geordnete Reihenfolge.
Sortieren Sie die dritte Zahl. Die Zahlen sind eingefügt, um eine neue geordnete Sequenz zu bilden.Wiederholen Sie den zweiten Schritt für die vierte Zahl, die fünfte Zahl ... bis zur letzten Zahl. 2.1. Singleton-Designmuster Funktionen: Singleton verhindert, dass die Klasse extern instanziiert wird, indem die Konstruktionsmethode auf privat beschränkt wird. Innerhalb derselben virtuellen Maschine kann die einzige Instanz von Singleton nur die Methode getInstance() übergeben . Zugang. (Tatsächlich ist es möglich, Klassen mit privaten Konstruktoren über den Java-Reflexionsmechanismus zu instanziieren, wodurch grundsätzlich alle Java-Singleton-Implementierungen ungültig werden.
Code:
Legen Sie zunächst die Anzahl der Einfügungen fest, dh die Anzahl der Schleifen. Für (int i=1;i
2. DesignmusterReferenz: Einige Designmuster in der Android-Entwicklung
Einzelfälle werden hauptsächlich unterteilt in: faule Leute Geben Sie Singleton ein, Singleton im hungrigen Stil, registrierter Singleton.
Lazy Singleton:
Es ist threadunsicher und gleichzeitig Es ist sehr wahrscheinlich, dass dort Um mehrere Singleton-Instanzen zu erreichen, gibt es drei Möglichkeiten:
1 Synchronisierung zur getInstance-Methode hinzufügen
3. Statische innere Klasse
Im Vergleich zu den ersten beiden Methoden erreicht diese Methode nicht nur Thread-Sicherheit, sondern vermeidet auch die durch die Synchronisierung verursachten Leistungseinbußen >Hungry-Style-Singleton:
Hungry-style hat beim Erstellen der Klasse bereits ein statisches Objekt für die Systemverwendung erstellt und wird es in Zukunft nicht ändern, sodass es von Natur aus systemsicher ist >
Das obige ist der detaillierte Inhalt vonDas ausführlichste Teilen von Android-Interviewfragen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!