Sunday, 11 October 2015

What is 65K Method limit in Android? How 65k Method limit handled by different build Systems of Android.

65k Method limit, well i recently faced this problem in one my android application. Trust me this is one of the worst problem you want to get into while development of your application.

So what is 65k method limit in Android? Firstly we should know this then only we can solve this.

65k Method Limit:

Apk i.e  Android Application contains DEX (dalvik executables i.e executable bytecode) as we know. So app contains a Single DEX i.e One Dex per app.

Single DEX file refers to 65,536 methods(including Android framework methods, library methods, and methods in your own code). So keep this thing in mind if your application is going to be big.

That's how error looks in Console =>
Earlier build versions :

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

Recent build versions :

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

So first thing we never want to get into this stitution. And if we're into this stitution then how can we get rid of this is as follows:

We need to generate more then One Dex file for our app which is known as a multidex configuration. In order to achieve this we need to configure our Build app.

This problem mostly occurs on version prior to 5.0. Beacuse earlier versions uses Dalvik runtime for executing code, where as 5+ uses ART(Android runtime).

So how these android runtime works when this 65k problem occurs.ART is present in 5+ versions which supports loading multi dex files from an apk. So we need to configure our build system for earlier version in order to implement multi dex concept for handling 65k error.
Solution to this problem is "Multidex Support Library".

We need to modify our gradle file firstly as follows :

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 21

    // Enabling multidex support.
        multiDexEnabled true
 
}

}


dependencies {
  compile 'com.android.support:multidex:1.0.0'
}


In your manifest add the MultiDexApplication class from the multidex support library .

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.multidex.myapplication">
    <application
        android:name="android.support.multidex.MultiDexApplication">
    </application>
</manifest>

So when These things are done Android build Tools will generate :

1.  Primary dex (classes.dex)
2.  Supporting (classes2.dex, classes3.dex) as needed.

The build system will then package them into an APK file for distribution.

So this how you can overcome 65k method problem. But best practice would be reducing code. you can try following things that i would recommend :

1. Remove large libs if included in your project. Beacuse you will be using few methods for your purpose rather then whole lib. So try avoid such things if you're facing such an issue.

2.Remove unused code via proguard.


References : https://developer.android.com/


Happy coding!!

Saturday, 10 October 2015

Toolbar Widget as Action Bar

Hi guys, Recently in my android projects i used toolbar alot. And most common use i found was showing a title with a back button at the top in the toolbar. Well i used both in Fragments and Activities. So how we can create a simple toolbar is as follows :



1. MainActivity
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;
    private TextView tvTitle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setToolBar();
    }

    private void setToolBar() {
        toolbar=(Toolbar)findViewById(R.id.toolbar);
        tvTitle=(TextView)findViewById(R.id.tv_title);
        tvTitle.setText("Toolbar Title");
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case android.R.id.home:
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}
2. activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <include
        layout="@layout/toolbar"/>

    <TextView android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>
3. toolbar.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
 android:minHeight="?attr/actionBarSize"
 app:theme="@style/ToolBarTheme">


        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
          android:minHeight="?attr/actionBarSize"
            android:layout_marginRight="?attr/actionBarSize"
            android:textAppearance="?android:textAppearanceMedium"
            android:textColor="@android:color/black" />

    </android.support.v7.widget.Toolbar>

</LinearLayout>

4. style.xml


<resources>

    <!-- Base application theme. --> 
   <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
    </style>
    <style name="ToolBarTheme" parent="Base.ThemeOverlay.AppCompat.Dark.ActionBar">
        <item name="android:textColorPrimary">#000000</item>
        <item name="android:textColorSecondary">#ffffff</item>
    </style>

</resources>

Github Link :- https://github.com/CammyKamal/BlogTutorials/tree/master/AddingToolbar

Google Map integration

In this post we will be having a look on how to integrate Google Maps in android. Here in this post, Google Map is integrating using dynamic fragment.


First Create project on https://console.developers.google.com/ and create a Android key using your SHA1 and Keystore (whether debug or release).

See Below Screenshots as reference for creating a Google web console project. And get your Map API key.










1. MapActivity

import android.location.Geocoder;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;

public class MapActivity extends FragmentActivity {
    private GoogleMap googleMap;
    private SupportMapFragment fragment;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);
        setupDynamicMapFragment();
    }

    private void setupDynamicMapFragment() {
        FragmentManager fm = this.getSupportFragmentManager();
        fragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
        if (fragment == null) {
            fragment = SupportMapFragment.newInstance();
            fm.beginTransaction().replace(R.id.map_container, fragment).commit();
        }
    }

    /*** function to load map. If map is not created it will create it for you     */   
 private void initializeMap() {
        LatLng location = null;
        if (googleMap == null) {
            googleMap = fragment.getMap();
            if (googleMap != null) {
                    location = new LatLng(30.7500,76.7800);
                if (location != null) {
                    CameraPosition cameraPosition = new CameraPosition.Builder().target(
                            location).zoom(12).build();
                    googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
                }
            }
        }
    }

//initializing Google Maps on resume in order to let the SupportMapfragment get initialized 
 @Override    protected void onResume() {
        super.onResume();
        initializeMap();
    }
}

2. activity_map.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/map_container"
    tools:context=".MapActivity">

    <!-- The map fragments will go here -->
</RelativeLayout>


3. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest 
  xmlns:android="http://schemas.android.com/apk/res/android"    
  package="com.Googlemapdemo" >
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--  The ACCESS_COARSE/FINE_LOCATION permissions are not required to use  Google Maps Android API v2, but are recommended.     -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- Required OpenGL ES 2.0. for Maps V2 -->    
<uses-feature        
 android:glEsVersion="0x00020000"
 android:required="true" />

  <application
    android:allowBackup="true"  
     android:icon="@mipmap/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" >
     <activity        
     android:name=".MapActivity"
     android:label="@string/app_name" >
       <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
      </activity>
      <meta-data
       android:name="com.google.android.gms.version"
       android:value="@integer/google_play_services_version" />
     <meta-data
      android:name="com.google.android.maps.v2.API_KEY"
      android:value="@string/google_maps_key" />

 </application>

</manifest>


4. strings.xml


<resources>
    <string name="app_name">MapDemo</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="google_maps_key">YOUR MAP KEY</string>
</resources>

5. build.gradle
apply plugin: 'com.android.application'
android {
    compileSdkVersion 23 
    buildToolsVersion "23.0.0 rc2"
    defaultConfig {
        applicationId "com.mapdemo"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.google.android.gms:play-services:7.5.0'}


Output