In android, you can define a class and extend with "Application" class. In this class you can declare app level fields and methods. In this class you also have access to the Application Context and there is a method which is invoked on the start of application. Sample of this is given below:
public class App extends Application {
private static Context sContext;
private static InterstitialAd mInterstitialAd;
public static Context getAppContext() {
return sContext;
}
public static InterstitialAd getInterstitialAd() {
return mInterstitialAd;
}
#Override
public void onCreate() {
super.onCreate();
sContext = getApplicationContext();
MobileAds.initialize(this, sContext.getString(R.string.ADMOB_APP_ID));
mInterstitialAd = new InterstitialAd(this);
mInterstitialAd.setAdUnitId(sContext.getString(R.string.interstitial));
mInterstitialAd.loadAd(new AdRequest.Builder().build());
mInterstitialAd.setAdListener(new AdListener() {
#Override
public void onAdClosed() {
mInterstitialAd.loadAd(new AdRequest.Builder().build());
}
});
}
}
What is corresponding thing in IOS?
In iOS, You can do configuration inside AppDelegate File in below method.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Here You Can Configure
return true
}
You can define class level methods in separate file in iOS and you can call it in didFinishLaunchingWithOptions() in AppDelegate file.
Related
I am generating a plugin in Flutter that will integrate a native library *.aar and this library needs to be initialized in the Application class of android, since it will extend the Application class of the library. The problem I am having is that at no time does it seem that the Application class is used in the Flutter plugin, I have tried to create the application class and define it in the plugin manifest, but at no time does it seem to enter the onCreate. I do not see how to solve this situation, any indication is welcome.
My class Application:
public class App extends SDKApplication{
#Override
public void onCreate() {
super.onCreate();
Log.e("App", "onCreate");
}
}
My manifest
<application
android:name=".App">
</application>
and the plugin class:
public class SdkPlugin implements FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel channel;
private EMTingSDK sdk = EMTingSDK.getInstance();
private Context context;
public SdkPlugin(){
Log.e("PRUEBAS","onMethodCall");
new App();
}
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
context = flutterPluginBinding.getApplicationContext();
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), Constants.CHANNEL);
channel.setMethodCallHandler(this);
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull Result result) {
Log.e("PRUEBAS","onMethodCall");
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
}else {
result.notImplemented();
}
}
#Override
public void onDetachedFromEngine(#NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
P.D: I need to start it in the Application because the developers of the native library told us that it had to be spent in this way, then I will put a little more code regarding the application.
public class SDKApplication extends CompanyApplication implements ActivityLifecycleCallbacks {
public SDKApplication() {
}
public void onCreate() {
SDK.getInstance().setContext(this.getApplicationContext());
super.onCreate();
Log.i("InfoSDK", "SDKApplication - init from App ");
this.registerActivityLifecycleCallbacks(this);
}
public void onActivityCreated(Activity activity, Bundle bundle) {
SDK.getInstance().setHostActivity(activity);
}
public void onActivityStarted(Activity activity) {
}
public void onActivityResumed(Activity activity) {
SDK.getInstance().setHostActivity(activity);
}
public void onActivityPaused(Activity activity) {
}
public void onActivityStopped(Activity activity) {
}
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
public void onActivityDestroyed(Activity activity) {
}
}
And the Company Application is:
public class CompanyApplication extends Application {
public CompanyApplication () {
}
public void onCreate() {
super.onCreate();
RequestQueue mainQueue = null;
if (SDK.getInstance().isDevelopEnviroment()) {
mainQueue = Volley.newRequestQueue(this.getApplicationContext(), new HurlStack((UrlRewriter)null, this.newSslSocketFactory()));
} else {
mainQueue = Volley.newRequestQueue(this.getApplicationContext());
}
CompanyHttpClient.getInstance().setMainQueue(mainQueue);
}
}
So, I declared my Worker Class NotifyWorker and called it in the MainActivity with the below code :
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.util.Log;
import com.birbit.android.jobqueue.JobManager;
import com.birbit.android.jobqueue.config.Configuration;
import com.birbit.android.jobqueue.log.CustomLogger;
public class MainActivity extends AppCompatActivity {
//some code
#Override
protected void onCreate(Bundle savedInstanceState) {
if(checkForUpdate(this)){
return;
}
//some code
final OneTimeWorkRequest notificationWork = new OneTimeWorkRequest.Builder(NotifyWorker.class)
.setInitialDelay(1, TimeUnit.MINUTES)
.addTag(workTag)
.build();
//WorkManager.getInstance(getBaseContext()).beginUniqueWork(workTag, ExistingWorkPolicy.REPLACE, notificationWork);
WorkManager.getInstance(getApplication()).enqueue(notificationWork);
Log.v(TAG,"Worker executed");
//Set content
setContentView(R.layout.activity_main);
//some code
}
}
So, I got the following error on running the app:
Caused by: java.lang.IllegalStateException: WorkManager is not initialized properly. You have explicitly disabled WorkManagerInitializer in your manifest, have not manually called WorkManager#initialize at this point, and your Application does not implement Configuration.Provider.
So, I followed The developer documentation , added the required code to my manifest, But i am unable to add the following method in myApplication() class :
class MyApplication extends Application implements Configuration.Provider {
#Override
public Configuration getWorkManagerConfiguration() {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}
My application class looks like below :
public class xxxApplication extends Application implements androidx.work.Configuration.Provider {
private static xxxApplication instance;
private JobManager jobManager;
private BroadcastReceiver broadcastReceiver;
public xxxApplication(){
instance = this;
}
#Override
public void onCreate(){
super.onCreate();
//ensure jobmanager is configured
getJobManager();
broadcastReceiver=new NetworkChangeReceiver();
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(broadcastReceiver, intentFilter);
}
#Override
public void onTerminate() {
super.onTerminate();
unregisterReceiver(broadcastReceiver);
}
private void configureJobManager(){
Configuration.Builder builder = new Configuration.Builder(this)
.customLogger(new CustomLogger() {
private static final String TAG = "JOBS";
#Override
public boolean isDebugEnabled() {
return true;
}
#Override
public void d(String text, Object... args) {
Log.d(TAG, String.format(text, args));
}
#Override
public void e(Throwable t, String text, Object... args) {
Log.e(TAG, String.format(text, args), t);
}
#Override
public void e(String text, Object... args) {
Log.e(TAG, String.format(text, args));
}
#Override
public void v(String text, Object... args) {
}
})
.minConsumerCount(1)
.maxConsumerCount(3)
.loadFactor(3)
.consumerKeepAlive(120);
//configure here if job scheduler involved
jobManager = new JobManager(builder.build());
}
public synchronized JobManager getJobManager() {
if (jobManager == null) {
configureJobManager();
}
return jobManager;
}
public static xxxApplication getInstance() {
return instance;
}
#Override
public **Configuration** getWorkManagerConfiguration() { //I get some conflict here , resolved by using androidx.work.Configuration as a return type
return Configuration.Builder() // Can't resolve the issue ?? what do i write here??
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}
I have commented the part where i get an issue , syntax error says "Method call expected" . How do I resolve this issue?? Why is it not working as expected? Is there a conflict between JobManager and WorkManager configurations?
UPDATE : So,there was actually a conflict between the configuration files of jobmanager and workmanager .Had to call the later using full location definition.
I am answering this myself, in case it is useful for others who have used Job-Schedulers before, and are now adding background processes using WorkManager .
While adding the extension as specified in the Developer documentation for custom initialization , use the following code in case you have already used JobScheduler in your application class :
public class xxxApplication extends Application implements androidx.work.Configuration.Provider {
//OTHER CODE
#Override
public androidx.work.Configuration getWorkManagerConfiguration() {
return new androidx.work.Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}
However, it is recommended that you migrate all the processes to WorkManager to avoid further conflicts.
I am using MVP pattern with a Fragment(GalleryFragment), where Application class(MainApplication) sources MainActivityRepository and GalleryFragmentPresenter(grouped as DIModules) which are provided to Fragment through field injection.
To test GalleryFragment in isolation, my idea was to use Robolectric configuration(#Config) to replace MainApplication entirely with a custom TestApplication sourcing mockDIModules.
GalleryFragmentTest runs until startFragment(galleryFragment) but I get a NullPointerException at MainApplication.getComponent().inject(this); inside GalleryFragment.
I suspect this is because this line specifically uses MainApplication while everything else is dealt with TestApplication set by Robolectric #Config, but I'm not sure and I am looking for advice on how to successfully run tests using this custom TestApplication.
While searching for possible solutions, I found out about using AndroidInjector from Dagger support library, which will get rid of MainApplication.getComponent().inject(this); entirely but would this work?
https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3
GalleryFragment.java
public class GalleryFragment extends Fragment {
#Inject
public MainActivityRepository mRepository;
#Inject
public GalleryFragmentPresenter mGalleryFragmentPresenter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainApplication.getComponent().inject(this); //NullPointerException here
mGalleryFragmentPresenter.initialize(mRepository, value);
}
}
DIModules.java
#Module
public class DIModules {
#Provides
public GalleryFragmentPresenter provideGalleryFragmentPresenter(){
return new GalleryFragmentPresenter();
}
#Provides
#Singleton
public MainActivityRepository provideMainActivityRepository(){
return new MainActivityRepository();
}
}
AppComponent.java
#Singleton
#Component(modules = DIModules.class)
public interface AppComponent {
void inject(GalleryFragment galleryFragment);
}
MainApplication.java
public class MainApplication extends Application {
public static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
Realm.init(this);
component = buildComponent();
}
public static AppComponent getComponent() {
return component;
}
protected AppComponent buildComponent(){
return DaggerAppComponent
.builder()
.dIModules(new DIModules())
.build();
}
}
TestApplication.java
public class TestApplication extends Application {
public static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = buildComponent();
}
public static AppComponent getComponent() {
return component;
}
protected AppComponent buildComponent(){
return DaggerAppComponent.builder()
.dIModules(new mockDIModules())
.build();
}
}
GalleryFragmentTest.java
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class,
application = TestApplication.class)
public class GalleryFragmentTest {
#Test
public void allItemTabTest() throws Exception {
GalleryFragment galleryFragment = GalleryFragment.newInstance(value);
startFragment(galleryFragment);
assertNotNull(galleryFragment);
}
}
I am using dagger, dagger-android-support, dagger-compiler version 2.14.1 and robolectric:3.6.1
Of course, it is null. Your fragment still tries to work with production application while you're doing things in the test application.
Change your injection code to next:
((MainApplication) getContext().getApplicationContext()).getComponent().inject(this);
And also make the method in your Application getComponent() as not static, so test app overrides it.
Another option is to change your TestApplication to next:
public class TestApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
buildComponent();
}
private void buildComponent(){
Application.component = DaggerAppComponent.builder()
.dIModules(new mockDIModules())
.build();
}
}
Here's my code, which I based on some old tutorial found on the internet. There really should be some examples on the main site of Dagger 2, I found it really difficult to understand how to implement all this.
It's really a lot of work to get such a simple app to run. I have two questions:
Do I have to call DaggerLoggerComponent in every class I want to get some components like my Logger class?
Also how can I make the scope of the Logger class a singleton? Right now every button click creates a new logger instance.
Probably I dont understand some underlying concepts, I've only used dependency injection in Spring before and all of this seems strange to me.
public class MainActivity extends AppCompatActivity {
private Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(){
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoggerComponent component = DaggerLoggerComponent.builder().loggerModule(new LoggerModule()).build();
component.getLogger().log("Hello!",MainActivity.this);
}
});
}
}
public class Logger {
private static int i = 0;
public Logger(){
i++;
}
public static int getI() {
return i;
}
public void log(String text, Context context){
Toast.makeText(context,text+" "+i,Toast.LENGTH_SHORT).show();
}
}
#Singleton
#Component(modules={LoggerModule.class})
public interface LoggerComponent {
Logger getLogger();
}
#Module
public class LoggerModule {
#Provides
#Singleton
Logger provideLogger(){
return new Logger();
}
}
The answer is
public class MainActivity extends AppCompatActivity {
#OnClick(R.id.button) //ButterKnife
public void onClickButton() {
logger.log("Hello!");
}
#Inject
Logger logger;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Injector.INSTANCE.getApplicationComponent().inject(this);
ButterKnife.bind(this);
}
#Override
protected void onDestroy() {
ButterKnife.unbind(this);
super.onDestroy();
}
}
public class Logger {
private static int i = 0;
private CustomApplication customApplication;
public Logger(CustomApplication application) {
this.customApplication = application;
i++;
}
public static int getI() {
return i;
}
public void log(String text){
Toast.makeText(customApplication, text + " " + i,Toast.LENGTH_SHORT).show();
}
}
public interface LoggerComponent {
Logger logger();
}
#Module
public class ApplicationModule {
private CustomApplication customApplication;
public ApplicationModule(CustomApplication customApplication) {
this.customApplication = customApplication;
}
#Provides
public CustomApplication customApplication() {
return customApplication;
}
}
#Module
public class LoggerModule {
#Provides
#Singleton
Logger provideLogger(){
return new Logger();
}
}
#Singleton
#Component(modules={LoggerModule.class, ApplicationModule.class})
public interface ApplicationComponent extends LoggerComponent {
CustomApplication customApplication();
void inject(MainActivity mainActivity);
}
public class CustomApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
Injector.INSTANCE.initializeApplicationComponent(this);
}
}
public enum Injector {
INSTANCE;
private ApplicationComponent applicationComponent;
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
void initializeApplicationComponent(CustomApplication customApplication) {
this.applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(customApplication))
.build();
}
}
This is currently our Dagger2 architecture.
EDIT: This is from our actual code for Retrofit stuff from our application we're making:
public interface RecordingService {
ScheduledRecordsXML getScheduledRecords(long userId)
throws ServerErrorException;
}
public class RecordingServiceImpl
implements RecordingService {
private static final String TAG = RecordingServiceImpl.class.getSimpleName();
private RetrofitRecordingService retrofitRecordingService;
public RecordingServiceImpl(RetrofitRecordingService retrofitRecordingService) {
this.retrofitRecordingService = retrofitRecordingService;
}
#Override
public ScheduledRecordsXML getScheduledRecords(long userId)
throws ServerErrorException {
try {
return retrofitRecordingService.getScheduledPrograms(String.valueOf(userId));
} catch(RetrofitError retrofitError) {
Log.e(TAG, "Error occurred in downloading XML file.", retrofitError);
throw new ServerErrorException(retrofitError);
}
}
}
#Module
public class NetworkClientModule {
#Provides
#Singleton
public OkHttpClient okHttpClient() {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new HeaderInterceptor());
return okHttpClient;
}
}
#Module(includes = {NetworkClientModule.class})
public class ServiceModule {
#Provides
#Singleton
public RecordingService recordingService(OkHttpClient okHttpClient, Persister persister, AppConfig appConfig) {
return new RecordingServiceImpl(
new RestAdapter.Builder().setEndpoint(appConfig.getServerEndpoint())
.setConverter(new SimpleXMLConverter(persister))
.setClient(new OkClient(okHttpClient))
.setLogLevel(RestAdapter.LogLevel.NONE)
.build()
.create(RetrofitRecordingService.class));
}
//...
}
public interface RetrofitRecordingService {
#GET("/getScheduledPrograms")
ScheduledRecordsXML getScheduledPrograms(#Query("UserID") String userId);
}
public interface ServiceComponent {
RecordingService RecordingService();
//...
}
public interface AppDomainComponent
extends InteractorComponent, ServiceComponent, ManagerComponent, ParserComponent {
}
#Singleton
#Component(modules = {
//...
InteractorModule.class,
ManagerModule.class,
ServiceModule.class,
ParserModule.class
//...
})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
void inject(DashboardActivity dashboardActivity);
//...
}
Do I have to call DaggerLoggerComponent in every class I want to get some components like my Logger class?
Yes for all classes that created by the system like Application, Activity and Service. but for you own classes, you don't need that. just annotate you constructor with #inject and dagger will provide your dependencies.
Also how can I make the scope of the Logger class a singleton? Right
now every button click creates a new logger instance.
Your setup for singleton is correct. but you have to initialize the component one time after the activity is created (onCreate) in order to let dagger to inject all fields. Also you can utilize lazy injection feature if you don't need the Logger object right away.
#Inject
Logger logger;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoggerComponent component = DaggerLoggerComponent.builder().loggerModule(new LoggerModule()).build();
component.inject(this);
init();
}
Then you can access your object without take the reference from the component:
private void init(){
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
logger.log("Hello!",MainActivity.this);
}
});
}
In summary:
You have to initialize the component in all classes that use field injections.
UPDATE:
To do the actual injection, you have to declare inject() method into your component and dagger will automatically implement it. This method will take care of provide any object annotated with #Inject.
Is it possible to inject different object through dagger into android.app.IntentService depending if it is a test or production?
this is mainly the code (simplified) which injects the WebRequest Class into the Service.
public class SomeService extends android.app.IntentService {
#Inject
WebReqeust mWebRequest;
public SomeService(String name) {
super(name);
MainApplication.getInstance().inject(this);
}
#Override
protected void onHandleIntent(Intent intent) {
String json = mWebRequest.getHttpString(url);
JSONObject o = new JSONObject(json);
DBHelper.insert(o);
}
}
#Module(injects = { SomeService.class })
public class WebRequestModule {
#Provides
WebRequest provideWebRequest() {
return new WebRequest();
}
}
public class Modules {
public static Object[] list() {
return new Object[] {
new WebRequestModule()
};
}
}
public class MainApplication extends Application {
private ObjectGraph mOjectGraph;
private static MainApplication sInstance;
#Override
public void onCreate() {
sInstance = this;
mOjectGraph = ObjectGraph.create(Modules.list());
}
public void inject(Object dependent) {
mOjectGraph.inject(dependent);
}
public void addToGraph(Object module) {
mOjectGraph.plus(module);
}
}
I would like to write a test which mocks the http response.
I've started with a new Module
#Module(
injects = SomeService.class,
overrides = true
)
final class MockTestModule {
#Provides
WebRequest provideWebRequest() {
WebRequest webRequest = mock(WebRequest.class);
when(webRequest.getJSONObjectResponse(contains("/register/"))).thenReturn(
new JSONObject(FileHelper.loadJSONFromAssets(this.getClass(),
"mock_register.json")));
when(webRequest.getJSONObjectResponse(contains("/register_validate/"))).thenReturn(
new JSONObject(FileHelper.loadJSONFromAssets(this.getClass(),
"mock_register_validate.json")));
return webRequest;
}
}
And in the test i tried the following
public class RegisterTest extends AndroidTestCase {
protected void setUp() throws Exception {
MainApplication.getInstance().addToGraph(new MockTestModule());
super.setUp();
}
public void test_theActuallTest() {
Registration.registerUser("email#email.com"); // this will start the service
wait_hack(); // This makes the test wait for the reposen form the intentservice, works fine
DBHelper.isUserRegisterd("email#email.com"));
}
}
The test is executed successfull (remember, the code is simplyfied and might not compile, just should represent the idea).
However, it still uses the "real" WebRequest Impl., not the Mocked one. I see it in the logs, the proxy and of ourse on the server ...
I did this with RoboGuice in a very similar way and it was working.
But somehow i am not able to get this done with dagger.
(I'm currently evaluating DI Frameworks and this is a "must have")
The plus method actual returns the new graph. It doesn't override the original graph. That being said to accomplish what you want you can simply do this.
public class MainApplication extends Application {
...
// Mostly used for testing
public void addToGraph(Object module) {
mObjectGraph = mOjectGraph.plus(module);
}
}
This takes the original graph and pluses it with your new module and then simply assigns the new graph to your mObjectGraph reference.