භ්‍රමණය වන ඇන්ඩ්‍රොයිඩ් මත ක්‍රියාකාරකම් නැවත ආරම්භ කරන්න


1387

මගේ ඇන්ඩ්‍රොයිඩ් යෙදුමේ, මම උපාංගය කරකවන විට (යතුරුපුවරුව ලිස්සා යන්න) එවිට මගේ Activityනැවත ආරම්භ වේ ( onCreateහැඳින්වේ). දැන්, මෙය බොහෝ විට එසේ විය යුතු ය, නමුත් මම onCreateක්‍රමයේ ආරම්භක සැකසුම් ගොඩක් කරමි, එබැවින් මටද අවශ්‍ය වන්නේ:

  1. සියලු ආරම්භක සැකසුම් වෙනත් ශ්‍රිතයකට දමන්න එවිට උපාංග භ්‍රමණය සියල්ලම නැති නොවේ
  2. එය සාදන්න onCreateනැවත කැඳවනු නොලබන අතර පිරිසැලසුම ගැලපේ
  3. යෙදුම හුදෙක් ආලේඛ්‍ය චිත්‍රයකට පමණක් සීමා කරන්න එවිට onCreateඑය කැඳවනු නොලැබේ.

4
මෙම බ්ලොග් සටහනේ ක්‍රියාකාරකම් වින්‍යාස වෙනස්වීම් වලදී දිගුකාලීන අසමමුහුර්ත කාර්යයන් රඳවා ගන්නේ කෙසේද යන්න පිළිබඳ තරමක් සම්පූර්ණ පැහැදිලි කිරීමක් තිබේ !
ඒඩ්‍රියන් හාමුදුරුවෝ

3
අනෙක් අය දැනටමත් පිළිතුරු දී ඇති පරිදි මෙය සෘජු පිළිතුරක් නොවේ, නමුත් ජීවන චක්‍ර සම්බන්ධයෙන් ඔබගේ ඇන්ඩ්‍රොයිඩ් යෙදුම්වල සිදුවන්නේ කුමක්ද යන්න තේරුම් ගැනීමට ලොග් ලයිෆ් සයිකල් දෙස බලන ලෙස මම ඔබට ආරාධනා කරමි .
ස්නිකොලාස්

Answers:


969

යෙදුම් පන්තිය භාවිතා කිරීම

ඔබේ ආරම්භයේ දී ඔබ කරන්නේ කුමක්ද යන්න මත පදනම්ව, නව පන්තියක් නිර්මාණය කිරීම Applicationසහ ඔබේ ආරම්භක කේතය onCreateඑම පන්තිය තුළ අභිබවා යන ක්‍රමයකට ගෙනයාම පිළිබඳව සලකා බැලිය හැකිය .

public class MyApplicationClass extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // TODO Put your application initialization code here.
  }
}

මෙම onCreateදිශානතිය හෝ යතුරු පුවරුව සැඟවීම් වෙනස්කම් මත ක්රියාකාරකම් නැවත ආරම්භ කර එය තුඩු දිය නෑ ඒ නිසා අයදුම්පත පන්තියේ පමණක්, සමස්ත අයදුම් නිර්මාණය කරන අවස්ථාවේදී ලෙස හැඳින්වේ.

මෙම පංතියේ නිදසුන සිංගල්ටන් එකක් ලෙස නිරාවරණය කිරීම සහ ඔබ ගේටර්ස් සහ සෙටර් භාවිතා කරමින් ආරම්භ කරන යෙදුම් විචල්‍යයන් නිරාවරණය කිරීම හොඳ පුරුද්දකි.

සටහන: ඔබේ නව යෙදුම් පන්තියේ නම ලියාපදිංචි කර භාවිතා කිරීම සඳහා මැනිෆෙස්ටයේ සඳහන් කළ යුතුය:

<application
    android:name="com.you.yourapp.MyApplicationClass"

වින්‍යාස වෙනස්වීම් වලට ප්‍රතික්‍රියා කිරීම [යාවත්කාලීන කිරීම: API 13 සිට මෙය ඉවත් කරනු ලැබේ; නිර්දේශිත විකල්පය බලන්න ]

තවත් විකල්පයක් ලෙස, නැවත ආරම්භ කිරීමට හේතු වන දිශානතිය සහ යතුරුපුවරු දෘශ්‍යතා වෙනස්වීම් වැනි සිදුවීම් වලට ඔබේ යෙදුම සවන් දිය හැකි අතර ඒවා ඔබගේ ක්‍රියාකාරකම් තුළ හැසිරවිය හැකිය.

android:configChangesඔබගේ ක්‍රියාකාරිත්වයේ මැනිෆෙස්ට් නෝඩයට නෝඩය එක් කිරීමෙන් ආරම්භ කරන්න

 <activity android:name=".MyActivity"
      android:configChanges="orientation|keyboardHidden"
      android:label="@string/app_name">

හෝ ඇන්ඩ්‍රොයිඩ් 3.2 සඳහා (API මට්ටම 13) සහ නවතම :

<activity android:name=".MyActivity"
      android:configChanges="keyboardHidden|orientation|screenSize"
      android:label="@string/app_name">

ක්‍රියාකාරකම තුළ onConfigurationChangedක්‍රමය ඉක්මවා ගොස් setContentViewGUI පිරිසැලසුම නව දිශානතිය තුළ නැවත සිදු කිරීමට බල කිරීමට අමතන්න .

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  setContentView(R.layout.myLayout);
}

17
දෙවන ප්‍රවේශය ක්‍රියාත්මක වේ යැයි මම නොසිතමි. මම එය උත්සාහ කළෙමි; EditText සමඟ එක් ක්‍රියාකාරකමක්. මම එහි යම් පෙළක් ලිව්වෙමි, දිශානතිය වෙනස් කළ අතර පෙළ නැතිවී / යළි පිහිටුවන ලදි.
ටෙඩ්

232
මෙන්න අපි බලාපොරොත්තු වන්නේ අනාගතයේදී අපි onRotate () ක්‍රමයක් දකිනු ඇති බවයි. මෙවැනි දේ ගැන කරදර වීමට පවා සිදුවීම - අවංකව - කලකිරීමට කරුණකි.
කෙලී සුටන්

84
ඇන්ඩ්‍රොයිඩ් ඩිව් මාර්ගෝපදේශය මෙය භාවිතා කිරීමට එරෙහිව අනතුරු අඟවන බව සලකන්න : සටහන: ( android:configChanges) භාවිතා කිරීම වළක්වා ගත යුතු අතර එය අවසාන පියවරක් ලෙස පමණක් භාවිතා කළ යුතුය. වින්‍යාස වෙනස්වීමක් හේතුවෙන් නැවත ආරම්භ කිරීම නිසි ලෙස හැසිරවිය යුතු ආකාරය පිළිබඳ වැඩි විස්තර සඳහා කරුණාකර ධාවන කාල වෙනස්වීම් හැසිරවීම කියවන්න. ඒ වෙනුවට, භ්‍රමණ සිදුවීම් හරහා දත්ත දිගටම පවත්වා ගැනීමට, ඔවුන් භාවිතා කිරීමට කැමැත්තක් දක්වන බව පෙනේ onSaveInstanceState Bundle; හෝ @ ජෝන්-ඕ සඳහන් කළ පරිදි , onRetainNonConfigurationInstance.
ජෙෆ්රෝ

19
එය නරක විසඳුමකි, මන්ද එය ප්‍රතික්‍රියා කරන්නේ දැනට දන්නා වින්‍යාස වෙනස්වීම් වලට පමණි. නව ඇන්ඩ්‍රොයිඩ් අනුවාද සමඟ, මෙම කේතය හසු නොවන වෙනත් වින්‍යාස වෙනස්වීම් සිදුවිය හැකිය (මන්ද එය මැනිෆෙස්ටයේ සියලුම වින්‍යාස වෙනස්කම් ලැයිස්තුගත කළ යුතුය). රාජ්‍යය සුරැකීමේ විසඳුම onRetainNonConfigurationChangesවඩාත් දෝෂ ඉවසා දරාගෙන කෙළින්ම ඉදිරියට යාමයි.
කෙසෙල්වෙයිසන්

16
ඔබේ පිළිතුරට ඔබ මෙම යාවත්කාලීනය 3.2 ට එකතු කළ යුතු යැයි මම සිතමි , එය බෙහෙවින් වැදගත් ය (එම ගැටලුවට මුහුණ දී ඇත) එය නොසලකා හැරිය හැක.
බිග්ස්ටෝන්ස්

185

ඇන්ඩ්‍රොයිඩ් 3.2 සහ ඊට වැඩි යාවත්කාලීන කිරීම:

අවවාදයයි : ඇන්ඩ්‍රොයිඩ් 3.2 (API මට්ටම 13) සිට, උපාංගය ආලේඛ්‍ය හා භූ දර්ශන දිශානතිය අතර මාරු වන විට “තිර ප්‍රමාණය” ද වෙනස් වේ. මේ අනුව, API මට්ටම 13 හෝ ඊට වැඩි (minSdkVersion සහ targetSdkVersion ගුණාංග මගින් ප්‍රකාශයට පත් කර ඇති පරිදි) සංවර්ධනය කිරීමේදී දිශානතියේ වෙනසක් හේතුවෙන් ධාවන නැවත ආරම්භ වීම වැළැක්වීමට ඔබට අවශ්‍ය නම්, ඔබ "screenSize"වටිනාකමට අමතරව අගය ඇතුළත් කළ යුතුය "orientation". එනම්, ඔබ ප්‍රකාශ කළ යුතුය android:configChanges="orientation|screenSize". කෙසේ වෙතත්, ඔබගේ යෙදුම API මට්ටම 12 හෝ ඊට අඩු ඉලක්ක කරන්නේ නම්, එවිට ඔබගේ ක්‍රියාකාරකම් සෑම විටම මෙම වින්‍යාස වෙනස් කිරීමම හසුරුවයි (ඇන්ඩ්‍රොයිඩ් 3.2 හෝ ඊට වැඩි උපාංගයක් මත ධාවනය වන විට පවා මෙම වින්‍යාස වෙනස් කිරීම ඔබගේ ක්‍රියාකාරකම් නැවත ආරම්භ නොකරයි).


1
එම පැහැදිලි කිරීම සඳහා ස්තූතියි, මේ පිළිබඳව ඉහත අදහස් දැක්වීම නිසා ඒ ගැන සොයා බැලීමට මා පිටත් විය. මම මේ වන විට API 8 ඉලක්ක කර ඇති අතර මගේ කේතයට වින්‍යාසගත කිරීම් වල තිර ප්‍රමාණය නොමැති අතර එය මා සතුව ඇති උපාංගයේ (නැවත දිශානතියකින් තොරව) හොඳින් ක්‍රියාත්මක වන බව සනාථ කළ හැකිය.
කාල්

මෙය පෙන්වා දීමට ස්තූතියි, මට තිබුණේ ඇන්ඩ්‍රොයිඩ් පමණි: configChanges = "දිශානතිය | තිර ප්‍රමාණය" කට්ටලය, සහ දිශානතිය මාරු කිරීම මගේ ක්‍රියාකාරකම ප්‍රතිනිර්මාණය කරන අතර, මගේ ජීවිත කාලය තුළ මට එය හඳුනා ගැනීමට නොහැකි විය!
ක්‍රිස්ටෝපර් පෙරී

5
Android එකතු කිරීම: configChanges භාවිතා කළ යුත්තේ අවසාන පියවරක් ලෙස පමණි . භාවිතා කිරීම Fragmentsසහ setRetainInstanceඒ වෙනුවට සලකා බලන්න .
සයිමන් ෆෝස්බර්ග්

ප්‍රධාන කරුණ වන්නේ screenSizeඇන්ඩ්‍රොයිඩ් 3.2 සහ ඊට වැඩි, මගේ ගැටලුව විසඳූ, ස්තූතියි!
fantouch

127

onCreate()වෙඩි තැබීම මුළුමනින්ම නැවැත්වීමට උත්සාහ කරනවා වෙනුවට Bundle savedInstanceState, සිදුවීම අවලංගු වී ඇත්දැයි බැලීමට උත්සාහ කරන්න .

නිදසුනක් වශයෙන්, Activityසෑම දිශානතියේ වෙනසක් මත නොව, සැබවින්ම නිර්මාණය කරන විට ක්‍රියාත්මක කළ යුතු යම් තර්කනයක් මා සතුව තිබේ නම් , මම එම තර්කනය ක්‍රියාත්මක කරන්නේ ශුන්‍ය onCreate()නම් පමණි savedInstanceState.

එසේ නොමැතිනම්, දිශානතිය සඳහා පිරිසැලසුම නිසි ලෙස නැවත ඇඳීමට මට තවමත් අවශ්‍යය.

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

        if(savedInstanceState == null){
            setupCloudMessaging();
        }
}

මෙය අවසාන පිළිතුර දැයි විශ්වාස නැත, නමුත් එය මට වැඩ කරයි.


6
ඔබ ඇත්තටම රාජ්‍යය ඉතිරි කරන්නේ කොහේද?
ඊවොක්ස්

5
මෙය මට වැඩ කරන බව පෙනේ, එය බොහෝ දුරට සරලම ක්‍රමය ලෙස පෙනේ. උප පංති යෙදුම පිළිබඳ අදහස සඳහා ඔබට මේ සඳහා ලැබුණේ 4 ක් පමණි (මගේද ඇතුළුව 5) 373 එදිරිව. මෙම ක්‍රමයේ අවාසියක් තිබේද?
ස්ටීව්

4
මෙම විසඳුම මට මහත් වැඩක් විය. මට හැකි විය Intent serverintent = new Intent(MainActivity.this, MessageListener.class);හා startService(serverintent);නිර්මාණය කිරීම serverSocket = new ServerSocket(0xcff2);හා Socket client = serverSocket.accept();සමඟ BufferedReader(new InputStreamReader(client.getInputStream()));මගේ ඇන්ඩ්රොයිඩ්, භ්රමණය හා ක්රියාකාරී වන සේවාලාභී / සේවාදායක සම්බන්ධතාව, තවමත් චිත්රක කරකැවී ඇති විය හැකි. අත්පොතට අනුව, අවසන් ක්‍රියාකාරකම වසා දැමූ විට saveInstanceState ආරම්භ වේ.
ෆ්‍රෙඩ් එෆ්

3
මට තේරෙන්නේ නැහැ, මොකක්ද අල්ලන්නේ? මෙය විශිෂ්ට ලෙස ක්‍රියා කරන අතර අනෙක් විසඳුම් වලට වඩා අඩු සංකීර්ණත්වයකින් යුක්ත වේ.
RTF

3
ඇන්ඩ්‍රොයිඩ් හි එය කිරීමට නිවැරදි ක්‍රමය මෙයයි. වින්‍යාසගත කිරීම් සමඟ භ්‍රමණය වන මූලික වශයෙන් අල්ලා ගැනීමේ අනෙක් ක්‍රම සහ විශාල, සංකීර්ණ හා අනවශ්‍ය සියල්ල.
ලූක් වැග්ගොනර්

99

මම මොනවද කළේ ...

මැනිෆෙස්ටයේ, ක්‍රියාකාරකම් අංශයට, එකතු කරන ලදි:

android:configChanges="keyboardHidden|orientation"

ක්‍රියාත්මක කිරීම සඳහා වූ කේතයේ, ක්‍රියාත්මක කර ඇත්තේ:

//used in onCreate() and onConfigurationChanged() to set up the UI elements
public void InitializeUI()
{
    //get views from ID's
    this.textViewHeaderMainMessage = (TextView) this.findViewById(R.id.TextViewHeaderMainMessage);

    //etc... hook up click listeners, whatever you need from the Views
}

//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    InitializeUI();
}

//this is called when the screen rotates.
// (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
@Override
public void onConfigurationChanged(Configuration newConfig)
{
    super.onConfigurationChanged(newConfig);
    setContentView(R.layout.main);

    InitializeUI();
}

3
පැහැදිලි කිරීම සඳහා: මගේ ක්‍රියාත්මක කිරීමත් සමඟ ඔබට දැන් onCreate () හි විචල්‍ය ආරම්භයක් ලබා ගත හැකි අතර onConfigurationChanged () තිරයේ භ්‍රමණය සඳහා කැඳවනු ලැබේ. ඔබගේ විචල්‍යයන් දැන් තිර භ්‍රමණයන්ගෙන් පරිවරණය කර ඇත ;-) නියමයි ez
කවුරුහරි කොහේ හරි

2
මෙහි විස්තර කර ඇති ආකාරයටම මම සෑම දෙයක්ම කළෙමි, නමුත් දිශානතිය වෙනස් වීමෙන් පසු බොත්තමක් එබීමට උත්සාහ කරන විට මට NullPointerException ලැබේ. වැරැද්ද කුමක් විය හැකිද?
ෆින්බෝයි 11

5
මතක තබා ගන්න මගේ පිළිතුර අවුරුදු 3 ක් හා ඇන්ඩ්‍රොයිඩ් පරිණාමය වෙමින් පවතී ... සයිමන් - ඔබට නියැදි කේතයට සබැඳියක් තිබේද? මිනිසුන්ට අවශ්‍ය වන්නේ එයයි.
කවුරුහරි කොහේ හරි

3
SimonAndréForsberg ඇත්තටම පමණක් paraphrasing ඇත @, configChanges: විට ඇන්ඩ්රොයිඩ් අනතුරු ඇන්ඩ්රොයිඩ් ලේඛන . ධාවන කාල වෙනස්වීම් හැසිරවීමේදී විකල්පයන් පිළිබඳ වඩාත් සවිස්තරාත්මක තොරතුරු ඇත (නියැදි කේතය ඇතුළුව).
ලීෆ් ආර්න් ස්ටෝර්සෙට්

67

ඔබ විස්තර කරන්නේ සුපුරුදු හැසිරීමයි. එකතු කිරීමෙන් මෙම සිදුවීම් ඔබම හඳුනාගෙන හැසිරවිය යුතුය:

android:configChanges

ඔබේ මැනිෆෙස්ටයට සහ පසුව ඔබට හැසිරවීමට අවශ්‍ය වෙනස්කම් වලට. එබැවින් දිශානතිය සඳහා, ඔබ භාවිතා කරන්නේ:

android:configChanges="orientation"

යතුරුපුවරුව විවෘත කිරීම හෝ වසා දැමීම සඳහා ඔබ භාවිතා කරන්නේ:

android:configChanges="keyboardHidden"

ඔබට දෙකම හැසිරවීමට අවශ්‍ය නම් ඔබට ඒවා පයිප්ප විධානයෙන් වෙන් කළ හැකිය:

android:configChanges="keyboardHidden|orientation"

මෙය ඔබ අමතන ඕනෑම ක්‍රියාකාරකමක onConfigurationChanged ක්‍රමය අවුලුවනු ඇත. ඔබ ක්‍රමවේදය ඉක්මවා ගියහොත් ඔබට නව අගයන් පසු කළ හැකිය.

මෙය උපකාරී වේ යැයි සිතමි.


3
@ ග්‍රෙග්ඩ් මම දනිමි, ඒ නිසා අද තත්වය පිළිබිඹු වන පරිදි එය යාවත්කාලීන කිරීමට හොඳ කාලයකි. මෙම ප්‍රශ්නයට ඇති ඉහළ ප්‍රතිශත ගණන සැලකිල්ලට ගෙන, එය තවමත් SO පිළිබඳ වෙනත් ප්‍රශ්න වලින් යොමු කරනු ලැබේ.
සයිමන් ෆෝස්බර්ග්

48

මම මේ ප්‍රබන්ධය සොයාගත්තා:

දිශානතියේ වෙනසක් හරහා ක්‍රියාකාරකම සජීවීව තබා ගැනීම සහ එය හැසිරවීම සඳහා onConfigurationChanged, ඉහත සඳහන් ප්‍රලේඛනය සහ කේත නියැදිය මැනිෆෙස්ට් ගොනුවේ යෝජනා කරයි:

<activity android:name=".MyActivity"
      android:configChanges="orientation|keyboardHidden"
      android:label="@string/app_name">

එය සැමවිටම ක්‍රියාත්මක වන අමතර ප්‍රතිලාභයක් ඇත.

ප්‍රසාද දීමනාව නම්, මඟ හැරීම keyboardHiddenතාර්කික යැයි පෙනෙන්නට තිබුණත්, එය ඉමියුලේටරයේ අසමත් වීමට හේතු වේ (ඇන්ඩ්‍රොයිඩ් 2.1 සඳහා අවම වශයෙන්): පමණක් සඳහන් කිරීමෙන් පමණක් orientationඉමුලේටරය ඇමතීමට හැකි වන OnCreateඅතර onConfigurationChangedසමහර විට සහ OnCreateවෙනත් වේලාවන් පමණි .

උපකරණයක අසමත් වීම මා දැක නැත, නමුත් අනෙක් අය සඳහා ඉමියුලේටරය අසමත් වීම ගැන මම අසා ඇත්තෙමි. එබැවින් එය ලේඛනගත කිරීම වටී.


15
අවවාදයයි: ඇන්ඩ්‍රොයිඩ් 3.2 (API මට්ටම 13) සිට, උපාංගය ආලේඛ්‍ය හා භූ දර්ශන දිශානතිය අතර මාරු වන විට “තිර ප්‍රමාණය” ද වෙනස් වේ. මේ අනුව, API මට්ටම 13 හෝ ඊට වැඩි සඳහා සංවර්ධනය කිරීමේදී දිශානතිය වෙනස් වීම හේතුවෙන් ධාවන වේලාව නැවත ආරම්භ කිරීම වළක්වා ගැනීමට ඔබට අවශ්‍ය නම්: android: configChanges = "දිශානතිය | යතුරු පුවරුව සැඟවුණු | තිර ප්‍රමාණය"
ජෙල්ට්‍රූඩ්

38

දිශානතියේ වෙනස්කම් හරහා ඇන්ඩ්‍රොයිඩ් වේදිකාවේ දත්ත දිගටම පවත්වා ගෙන යාමේ ක්‍රමය භාවිතා කිරීම ගැනද ඔබට සලකා බැලිය හැකිය: onRetainNonConfigurationInstance()සහ getLastNonConfigurationInstance().

මෙය ඔබ සේවාදායකයෙන් ලබාගෙන ඇති තොරතුරු onCreateහෝ ගණනය කර ඇති හෝ වෙනත් දෙයක් වැනි වින්‍යාස වෙනස්වීම් හරහා දත්ත දිගටම පවත්වා ගැනීමට ඉඩ සලසයි , එසේම Activityදැන් භාවිතයේ පවතින දිශානතිය සඳහා ඔබේ xml ගොනුව නැවත සැකසීමට ඇන්ඩ්‍රොයිඩ්ට ඉඩ දෙයි. .

මෙතැනින් හෝ මෙතැනින් බලන්න .

මෙම ක්‍රම දැන් අතහැර දමා ඇති බව සැලකිල්ලට ගත යුතුය (ඉහත විසඳුම් බොහොමයක් යෝජනා කරන පරිදි දිශානතිය හැසිරවීමට වඩා නම්‍යශීලී වුවද) ඔබ රඳවා තබා ගැනීමට අවශ්‍ය සෑම කෙනෙකුටම මාරුවිය යුතු Fragmentsඅතර ඒ වෙනුවට භාවිතා setRetainInstance(true)කළ Fragmentයුතුය.


4
මම හිතන්නේ ෆ්‍රැග්මන්ට්ස් සහ සෙට් රීටයින් ඉන්ස්ටාන්ස් මෙය කිරීමට හොඳම ක්‍රමය (සහ ගූගල් විසින් නිර්දේශිත ක්‍රමය), ඔබට +1 සහ අනෙක් සියල්ලටම -1. Android එකතු කිරීම: configChanges භාවිතා කළ යුත්තේ අවසාන
සයිමන් ෆෝස්බර්ග්

32

ප්රවේශය ප්රයෝජනවත් නමුත් කොටස් භාවිතා කිරීමේදී අසම්පූර්ණයි.

කොටස් සාමාන්‍යයෙන් වින්‍යාස වෙනස් කිරීම මත ප්‍රතිනිර්මාණය වේ. මෙය සිදුවීමට ඔබ අකමැති නම්, භාවිතා කරන්න

setRetainInstance(true); කැබැල්ලේ ඉදිකිරීම්කරු (ය) තුළ

මෙය වින්‍යාස වෙනස් කිරීමේදී කොටස් රඳවා තබා ගැනීමට හේතු වේ.

http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)


8
එකඟ විය. නවතම ඇන්ඩ්‍රොයිඩ් ඒපීඅයි සමඟ, මෙය හැසිරවිය හැකි නිවැරදි ක්‍රමය කොටස් බව පෙනේ. මම තවම එය අත්හදා බැලුවේ නැත, නමුත් මම මෙම පිටුව කියවා ඇති දෙයින් , ඔබ මූලික වශයෙන් ක්‍රියාකාරකමක ක්‍රියාත්මක කිරීමට භාවිතා කළ දෙයින් 99% ක්ම කැබැල්ලක උප පංතියකට ගෙන ගොස් එම කොටස ක්‍රියාකාරකමට එක් කරන්න. තිරයේ භ්‍රමණය මත ක්‍රියාකාරකම තවමත් විනාශ වී ප්‍රතිනිර්මාණය වනු ඇත, නමුත් b ඇබ්ඩෝ සඳහන් කළ ක්‍රමය භාවිතා කරමින් කැබැල්ල විනාශ නොකරන ලෙස ඔබට විශේෂයෙන් ඇන්ඩ්‍රොයිඩ්ට පැවසිය හැකිය setRetainInstance().
බ්‍රයන්මර්න්ස්

25

මම සරලවම එකතු කළා

     android:configChanges="keyboard|keyboardHidden|orientation"

මැනිෆෙස්ට් ගොනුවේ ඇති අතර මගේ ක්‍රියාකාරකමේ කිසිදු ක්‍රමයක් එකතු කර නැත onConfigurationChanged.

එබැවින් යතුරුපුවරුව ලිස්සා යන සෑම අවස්ථාවකම කිසිවක් සිදු නොවේ .


19

onCreateඔබ orientationඇන්ඩ්‍රොයිඩ් වෙනස් කළ විට පවා මෙම ක්‍රමය හැඳින්වේ . එබැවින් සියලු බර ක්‍රියාකාරිත්වය මෙම ක්‍රමයට ගෙනයාම ඔබට උපකාරී නොවේ



17
 onConfigurationChanged is called when the screen rotates. 
 (onCreate is no longer called when screen rotates due to manifest, see:  
 android:configChanges)

මැනිෆෙස්ටයේ කුමන කොටස එය "අමතන්න එපා onCreate()" යැයි පවසයි?

එසේම, ගූගල්ගේ ලියකියවිලි භාවිතා කිරීමෙන් වළකින්නැයි කියනු ලැබේ android:configChanges(අවසාන පියවරක් හැර) .... නමුත් පසුව ඔවුන් යෝජනා කරන විකල්ප ක්‍රම සියල්ලම DO භාවිතා කිරීමට යෝජනා කරයි android:configChanges.

ඉමියුලේටරය සෑම විටම onCreate()භ්‍රමණය ඉල්ලා සිටින බව මගේ අත්දැකීමයි .
නමුත් මම එකම කේතය ක්‍රියාත්මක කරන උපාංග 1-2 ... එසේ නොකරන්න. (කිසියම් වෙනසක් සිදුවන්නේ මන්දැයි විශ්වාස නැත.)


17

පහත දැක්වෙන පියවරයන් අනුගමනය කිරීම ඉතා සරල ය:

<activity
    android:name=".Test"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="landscape" >
</activity>

මෙය මට වැඩ කරයි:

සටහන: දිශානතිය ඔබේ ඉල්ලීම මත රඳා පවතී


16

ඇන්ඩ්‍රොයිඩ් මැනිෆෙස්ටයේ කළ යුතු වෙනස්කම්:

android:configChanges="keyboardHidden|orientation" 

ක්‍රියාකාරකම් ඇතුළත කළ යුතු එකතු කිරීම්:

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

16

ඔබේ මැනිෆෙස්ටයට මෙම රේඛාව එක් කරන්න: -

android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

සහ ක්‍රියාකාරකම් සඳහා මෙම ස්නිපටය: -

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

15

මෙය කිරීමට ක්‍රම කිහිපයක් තිබේ:

ක්‍රියාකාරකම් තත්වය සුරකින්න

ඔබට ක්‍රියාකාරී තත්වය සුරැකිය හැක onSaveInstanceState.

@Override
public void onSaveInstanceState(Bundle outState) {
    /*Save your data to be restored here
    Example : outState.putLong("time_state", time); , time is a long variable*/
    super.onSaveInstanceState(outState);
}

ඉන්පසු bundleතත්වය යථා තත්වයට පත් කිරීමට භාවිතා කරන්න .

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(savedInstanceState!= null){
       /*When rotation occurs
        Example : time = savedInstanceState.getLong("time_state", 0); */
    } else {
      //When onCreate is called for the first time
    }
}

දිශානතියේ වෙනස්කම් ඔබ විසින්ම හසුරුවන්න

තවත් විකල්පයක් වන්නේ දිශානතියේ වෙනස්කම් ඔබ විසින්ම හසුරුවා ගැනීමයි. නමුත් මෙය හොඳ පුරුද්දක් ලෙස නොසැලකේ.

මෙය ඔබේ මැනිෆෙස්ට් ගොනුවට එක් කරන්න.

android:configChanges="keyboardHidden|orientation"

Android 3.2 සහ ඊට පසුව:

android:configChanges="keyboardHidden|orientation|screenSize"

@Override
public void onConfigurationChanged(Configuration config) {
    super.onConfigurationChanged(config);

if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        //Handle rotation from landscape to portarit mode here
    } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
        //Handle rotation from portrait to landscape mode here
    }
}

භ්‍රමණය සීමා කරන්න

භ්‍රමණය වීම වළක්වා ගැනීම සඳහා ඔබේ ක්‍රියාකාරකම් ආලේඛ්‍ය හෝ භූ දර්ශන මාදිලියට සීමා කළ හැකිය.

ඔබගේ මැනිෆෙස්ට් ගොනුවේ ක්‍රියාකාරකම් ටැගයට මෙය එක් කරන්න:

        android:screenOrientation="portrait"

නැතහොත් මෙය ඔබගේ ක්‍රියාකාරකම තුළ ක්‍රමලේඛිකව ක්‍රියාත්මක කරන්න:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

12

මෙය කිරීමට මා සොයාගෙන ඇති ක්‍රමය වන්නේ යම් දෙයක් සුරැකීමට onRestoreInstanceStateසහ onSaveInstanceStateසිදුවීම් භාවිතා කිරීමයි Bundle(ඔබට කිසිදු විචල්‍යයක් සුරැකීමට අවශ්‍ය නොවුවද, යමක් එහි තබන්න, එවිට Bundleඑය හිස් නොවේ). ඉන්පසුව, onCreateක්‍රමවේදය මත , Bundleඑය හිස් දැයි බැලීමට පරීක්ෂා කරන්න , එය එසේ නම්, ආරම්භක කිරීම කරන්න, එසේ නොවේ නම් එය කරන්න.


12

එය "ඇන්ඩ්‍රොයිඩ් ක්‍රමය" නොවුනත්, දිශානතියේ වෙනස්කම් හසුරුවමින් සහ වෙනස් කළ දිශානතිය සැලකිල්ලට ගැනීම සඳහා විජට් නැවත ස්ථානගත කිරීමෙන් මම ඉතා හොඳ ප්‍රති results ල ලබා ඇත්තෙමි. මෙය වෙනත් ඕනෑම ප්‍රවේශයකට වඩා වේගවත් ය, මන්ද ඔබේ අදහස් සුරැකීමට සහ ප්‍රතිෂ් .ාපනය කිරීමට අවශ්‍ය නොවන බැවිනි. එය පරිශීලකයාට වඩාත් අසීමිත අත්දැකීමක් ලබා දෙයි, මන්දයත් ප්‍රතිස්ථාපිත විජට් හරියටම එකම විජට් වන අතර, දැන් ගෙන ගොස් / හෝ ප්‍රමාණය වෙනස් කර ඇත. ආදර්ශ රාජ්ය පමණක් නොව, රාජ්යය ද මේ ආකාරයෙන් සංරක්ෂණය කළ හැකිය.

RelativeLayoutවරින් වර නැවත වෙනස් කළ යුතු දර්ශනයක් සඳහා සමහර විට හොඳ තේරීමක් විය හැකිය. සෑම ළමා විජට් එකක් සඳහාම ඔබ එකිනෙකට වෙනස් සාපේක්ෂ ස්ථානගත කිරීමේ රීති සහිත පෝට්රේට් පිරිසැලසුම් පරාමිතීන් සහ භූ දර්ශන පිරිසැලසුම් පරාමිති කට්ටලයක් ලබා දෙයි. ඉන්පසු, ඔබේ onConfigurationChanged()ක්‍රමයේදී, ඔබ setLayoutParams()සෑම දරුවෙකුටම ඇමතුමකට සුදුසු එකක් ලබා දේ . ඕනෑම ළමා පාලනයක් අභ්‍යන්තරව ප්‍රතිනිර්මාණය කිරීමට අවශ්‍ය නම්, ඔබ එම දරුවාට නැවත සකස් කිරීම සඳහා ක්‍රමවේදයක් අමතන්න. එම දරුවා හා සමාන ඕනෑම මත ක්රම ඉල්ලා එහි සහ එසේ මත, අභ්යන්තර නව මගකට යොමු අවශ්ය බව ළමා පාලනය.


මෙහි නියැදි කේත කිහිපයක් දැකීමට මා කැමතිය.
හෙන්රික් ද සොසා

9

තිරය ​​භ්‍රමණය වන සෑම අවස්ථාවකම, විවෘත කළ ක්‍රියාකාරකම් අවසන් වී onCreate () නැවත කැඳවනු ලැබේ.

1. තිරය ​​භ්‍රමණය වන විට ක්‍රියාකාරීත්වයේ තත්වය සුරැකීමට ඔබට එක් දෙයක් කළ හැකි අතර, එමඟින් ක්‍රියාකාරීත්වයේ onCreate () නැවත කැඳවූ විට ඔබට සියලු පැරණි දේවල් නැවත ලබා ගත හැකිය. මෙම සබැඳිය බලන්න

2. ක්‍රියාකාරකම නැවත ආරම්භ කිරීම වලක්වා ගැනීමට ඔබට අවශ්‍ය නම් පහත දැක්වෙන රේඛා ඔබේ මැනිෆෙස්ට්.

  <activity android:name=".Youractivity"
  android:configChanges="orientation|screenSize"/>

8

එහි පරාමිතියෙහි සියලු අගයන් ගබඩා කිරීම සඳහා ඔබ onSavedInstanceState ක්‍රමය භාවිතා කළ යුතුය.

@Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        outPersistentState.putBoolean("key",value);
    }

සහ භාවිතා කරන්න

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        savedInstanceState.getBoolean("key");
    } 

නැවත ලබා ගැනීමට සහ වස්තු බැලීමට අගය සැකසීමට එය තිර භ්‍රමණයන් හසුරුවනු ඇත


7

සටහන: අනාගතයේ යමෙකු මා හා සමාන ගැටලුවකට මුහුණ දෙන්නේ නම් මම මෙම පිළිතුර පළ කරමි. මට නම් පහත දැක්වෙන පේළිය ප්‍රමාණවත් නොවේ:

android:configChanges="orientation"

මම තිරය භ්‍රමණය කරන විට, `onConfigurationChanged (Configuration newConfig) ක්‍රමය ඇමතුවේ නැත.

විසඳුම: දිශානතිය සමඟ ගැටළුව සම්බන්ධ වුවද මට "තිර ප්‍රමාණය" එකතු කිරීමට සිදුවිය. එබැවින් AndroidManifest.xml - ගොනුවේ මෙය එකතු කරන්න:

android:configChanges="keyboardHidden|orientation|screenSize"

ඉන්පසු ක්රමය ක්රියාත්මක කරන්න onConfigurationChanged(Configuration newConfig)




5

මිනිස්සු කියන්නේ ඔබ භාවිතා කළ යුතු බවයි

android:configChanges="keyboardHidden|orientation"

නමුත් ඇන්ඩ්‍රොයිඩ් හි භ්‍රමණය හැසිරවිය හැකි හොඳම හා වඩාත්ම වෘත්තීය ක්‍රමය වන්නේ ලෝඩර් පන්තිය භාවිතා කිරීමයි. එය ප්‍රසිද්ධ පංතියක් නොවේ (ඇයිදැයි මම නොදනිමි), නමුත් එය අසින්ක් කාර්යයට වඩා හොඳය. වැඩි විස්තර සඳහා, ඔබට Udacity හි Android පා .මාලා වල ඇති Android නිබන්ධන කියවිය හැකිය.

ඇත්ත වශයෙන්ම, වෙනත් ක්‍රමයක් ලෙස, ඔබට සාරධර්ම හෝ අදහස් onSaveInstanceState සමඟ ගබඩා කර onRestoreInstanceState සමඟ කියවිය හැකිය. එය ඇත්තෙන්ම ඔබට භාරයි.


ඔව්, "වෘත්තීය" ලෙස පෙනීම සඳහා අමතර කේත ගොබ්බ එකතු කරමු. නැතහොත් වින්‍යාස වෙනස්වීම් ගුණාංගය සමඟ ඉක්මන්, පහසු, සත්‍ය සහ උත්සාහ කළ ක්‍රමයට ඇලී සිටින්නේ කෙසේද?
AndroidDev

4

අත්හදා බැලීම් සහ දෝෂයන්ගෙන් ටික කලකට පසු, බොහෝ අවස්ථාවලදී මගේ අවශ්‍යතාවන්ට ගැලපෙන විසඳුමක් මට හමු විය. මෙන්න කේතය:

ප්‍රතිපත්ති වින්‍යාසය:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.pepperonas.myapplication">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

ප්‍රධාන ක්‍රියාකාරිත්වය:

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";

    private Fragment mFragment;

    private int mSelected = -1;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate  " + "");

        // null check not realy needed - but just in case...
        if (savedInstanceState == null) {

            initUi();

            // get an instance of FragmentTransaction from your Activity
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            /*IMPORTANT: Do the INITIAL(!) transaction only once!
            * If we call this everytime the layout changes orientation,
            * we will end with a messy, half-working UI.
            * */
            mFragment = FragmentOne.newInstance(mSelected = 0);
            fragmentTransaction.add(R.id.frame, mFragment);
            fragmentTransaction.commit();
        }
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged  " +
                   (newConfig.orientation
                    == Configuration.ORIENTATION_LANDSCAPE
                    ? "landscape" : "portrait"));

        initUi();

        Log.i(TAG, "onConfigurationChanged - last selected: " + mSelected);
        makeFragmentTransaction(mSelected);
    }


    /**
     * Called from {@link #onCreate} and {@link #onConfigurationChanged}
     */
    private void initUi() {
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate  instanceState == null / reinitializing..." + "");
        Button btnFragmentOne = (Button) findViewById(R.id.btn_fragment_one);
        Button btnFragmentTwo = (Button) findViewById(R.id.btn_fragment_two);
        btnFragmentOne.setOnClickListener(this);
        btnFragmentTwo.setOnClickListener(this);
    }


    /**
     * Not invoked (just for testing)...
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME!!!");
    }


    /**
     * Not invoked (just for testing)...
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME, AS WELL!!!");
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume  " + "");
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause  " + "");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy  " + "");
    }


    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_fragment_one:
                Log.d(TAG, "onClick btn_fragment_one " + "");
                makeFragmentTransaction(0);
                break;

            case R.id.btn_fragment_two:
                Log.d(TAG, "onClick btn_fragment_two " + "");
                makeFragmentTransaction(1);
                break;

            default:
                Log.d(TAG, "onClick  null - wtf?!" + "");
        }
    }


    /**
     * We replace the current Fragment with the selected one.
     * Note: It's called from {@link #onConfigurationChanged} as well.
     */
    private void makeFragmentTransaction(int selection) {

        switch (selection) {
            case 0:
                mFragment = FragmentOne.newInstance(mSelected = 0);
                break;
            case 1:
                mFragment = FragmentTwo.newInstance(mSelected = 1);
                break;
        }

        // Create new transaction
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        // Replace whatever is in the fragment_container view with this fragment,
        // and add the transaction to the back stack
        transaction.replace(R.id.frame, mFragment);

        /*This would add the Fragment to the backstack...
        * But right now we comment it out.*/
        //        transaction.addToBackStack(null);

        // Commit the transaction
        transaction.commit();
    }

}

සහ නියැදි කොටස්:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * @author Martin Pfeffer (pepperonas)
 */
public class FragmentOne extends Fragment {

    private static final String TAG = "FragmentOne";


    public static Fragment newInstance(int i) {
        Fragment fragment = new FragmentOne();
        Bundle args = new Bundle();
        args.putInt("the_id", i);
        fragment.setArguments(args);
        return fragment;
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView  " + "");
        return inflater.inflate(R.layout.fragment_one, container, false);
    }

}

ගිතූබ් හි සොයාගත හැකිය .


4

orientationවිවිධ දිශානතිය මත විවිධ කාර්යයන් ඉටු කිරීමට සවන්දෙන්නන් භාවිතා කරන්න .

@Override
public void onConfigurationChanged(Configuration myConfig) 
{
    super.onConfigurationChanged(myConfig);
    int orient = getResources().getConfiguration().orientation; 
    switch(orient) 
    {
       case Configuration.ORIENTATION_LANDSCAPE:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    break;
       case Configuration.ORIENTATION_PORTRAIT:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
       default:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
}

4

ඔබේ කේතය පහත මෙම දාන්න Activityදී Android Manifest.

android:configChanges="orientation"

ඔබ දිශානතිය වෙනස් කරන විට මෙය ඔබගේ ක්‍රියාකාරකම් නැවත ආරම්භ නොවේ.


3
AvMavamaarten බොහෝ විට අනෙක් අය පෙන්වා දී ඇති පරිදි, එය නරක උපහාසයක් වන අතර තවත් පිළිතුරු දහයක් දැනටමත් මෙය ආවරණය කර ඇත.
මිකෝප්

4

තිරයේ දිශානතිය (භූ දර්ශනය හෝ ආලේඛ්‍ය චිත්‍රය) සවි කරන්න AndroidManifest.xml

android:screenOrientation="portrait" හෝ android:screenOrientation="landscape"

මේ සඳහා ඔබේ onResume()ක්‍රමය කැඳවනු නොලැබේ.


6
අපායක් යමක් සවි කිරීම පිළිතුරක් වන්නේ කෙසේද? අපි එය භාවිතා කරන්නන් අගුළු දැමුවහොත් අපගේ උපාංග භ්‍රමණය විය හැක්කේ ඇයි?
රයින්හර්ඩ්

4

ගූගල් විසින් හඳුන්වා දෙන ලද ඇන්ඩ්‍රොයිඩ් ගෘහ නිර්මාණ ශිල්පයේ හොඳම අංගයක් වන්නේ ඔබේ සියලු අවශ්‍යතාවයන් වන ViewModel සපුරාලීමයි.

එය නිර්මාණය කර ඇත්තේ UI ආශ්‍රිත දත්ත ජීවන චක්‍රයේ ගබඩා කිරීම සහ කළමනාකරණය කිරීම සඳහා වන අතර එමඟින් තිරය භ්‍රමණය වන විට දත්ත නොනැසී පවතිනු ඇත

class MyViewModel : ViewModel() {

කරුණාකර මෙය යොමු කරන්න: https://developer.android.com/topic/libraries/architecture/viewmodel


2

ඔබගේ ක්‍රියාකාරකම තුළ ඔබට ViewModel වස්තුව භාවිතා කළ හැකිය.

වින්‍යාස වෙනස්වීම් වලදී ViewModel වස්තූන් ස්වයංක්‍රීයව රඳවා තබා ගන්නා අතර එමඟින් ඔවුන් සතුව ඇති දත්ත ඊළඟ ක්‍රියාකාරකමට හෝ කොටස් නිදසුනක් සඳහා වහාම ලබා ගත හැකිය. වැඩිදුර කියවන්න:

https://developer.android.com/topic/libraries/architecture/viewmodel

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.